How do I remove all selected nodes from an XPath? - java

I run an XPath in Java with the following xml and code:
<?xml version="1.0" encoding="UTF-8"?>
<list>
<member name="James">
<friendlist>
<friend>0001</friend>
<friend>0002</friend>
<friend>0003</friend>
</friendlist>
</member>
<member name="Jamie">
<friendlist>
<friend>0003</friend>
<friend>0002</friend>
<friend>0001</friend>
</friendlist>
</member>
<member name="Katie">
<friendlist>
<friend>0001</friend>
<friend>0003</friend>
<friend>0004</friend>
</friendlist>
</member>
</list>
Code:
try {
XPath xpath = XPathFactory.newInstance().newXPath();
XPathExpression pathExpr = xpath.compile("/list/member/friendlist/friend[.='0003']");
} catch (XPathExpressionException e) {
Of course there are more codes after this but I didn't paste it here because it thought it may confuse even more.
But the idea is I wish to select all the friend nodes that have the ID 0003 from all the members' friendlist nodes, and then remove it from the XML file. The XPath works by selecting all the "friend" nodes that have the value=0003. I know I can use the removeChild() method of the XML Document object. But the problem is how do I remove all of it directly, without going through layers of loops starting from its parent? The removeChild() method needs me to know its parent's parent's parent.
Thanks!
Update:
This is how I used my XPath:
XPath xpath = XPathFactory.newInstance().newXPath();
XPathExpression pathExpr = null;
try {
pathExpr = xpath.compile("/list/member/friendlist/friend[.='0003']");
} catch (XPathExpressionException e) {
e.printStackTrace();
}
NodeList list = null;
try {
list = (NodeList) pathExpr.evaluate(xmlDoc, XPathConstants.NODESET);
} catch (XPathExpressionException e) {
e.printStackTrace();
}
The xmlDoc is an XML document object that has an XML file parsed. The XML works fine. It is only the XML not returning a reference but a whole new nodelist, which makes it impossible for me to refer back to its original xml document to do amendments.

for each node in the returned NodeList:
n.getParentNode().removeChild(n);

I don't understand why the returned nodelist's nodes are returning null for parentNode().
But you could try first selecting all the parents of the nodes you want to remove, with this XPath expression:
"/list/member/friendlist[friend[.='0003']]"
or the equivalent,
"/list/member/friendlist[friend = '0003']]"
Then iterate through the resulting nodelist, and in the context of each one, query for nodes matching the XPath expression
"friend[.='0003']"
That will give you a parent node and a child node to use with removeChild().

Have a look on XUpdate. It's not pretty, but it works.

Related

Getting null values from XPath query

I have this xml file:
<?xml version="1.0" encoding="UTF-8"?>
<iet:aw-data xmlns:iet="http://care.aw.com/IET/2007/12" class="com.aw.care.bean.resource.MessageResource">
<iet:metadata filter=""/>
<iet:message-resource>
<iet:message>some message 1</iet:message>
<iet:customer id="1"/>
<iet:code>edi.claimfilingindicator.11</iet:code>
<iet:locale>iw_IL</iet:locale>
</iet:message-resource>
<iet:message-resource>
<iet:message>some message 2</iet:message>
<iet:customer id="1"/>
<iet:code>edi.claimfilingindicator.12</iet:code>
<iet:locale>iw_IL</iet:locale>
</iet:message-resource>
.
.
.
.
</iet:aw-data>
Using this code below i'm getting over the data and finding what I need.
try {
FileInputStream fileIS = new FileInputStream(new File("resources\\bootstrap\\content\\MessageResources_iw_IL\\MessageResource_iw_IL.ctdata.xml"));
DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();
builderFactory.setNamespaceAware(true); // never forget this!
DocumentBuilder builder = builderFactory.newDocumentBuilder();
Document xmlDocument = builder.parse(fileIS);
XPath xPath = XPathFactory.newInstance().newXPath();
String query = "//*[local-name()='message-resource']//*[local-name()='code'][contains(text(), 'account')]";
NodeList nodeList = (NodeList) xPath.compile(query).evaluate(xmlDocument, XPathConstants.NODESET);
System.out.println("size= " + nodeList.getLength());
for (int i = 0; i < nodeList.getLength(); i++) {
System.out.println(nodeList.item(i).getNodeValue());
}
}
catch (Exception e){
e.printStackTrace();
}
The issue is that i'm getting only null values while printing in the for loop, any idea why it's happened?
The code needs to return a list of nodes which have a code and message fields that contains a given parameters (same as like SQL query with two parameters with operator of AND between them)
Check the documentation:
https://docs.oracle.com/javase/7/docs/api/org/w3c/dom/Node.html
getNodeValue() applied to an element node returns null.
Use getTextContent().
Alternatively, if you find DOM too frustrating, switch to one of the better tree models like JDOM2 or XOM.
Also, if you used an XPath 2.0 engine like Saxon, it would (a) simplify your expression to
//*:message-resource//*:code][contains(text(), 'account')]
and (b) allow you to return a sequence of strings from the XPath expression, rather than a sequence of nodes, so you wouldn't have to mess around with nodelists.
Another point: I suspect that the predicate [contains(text(), 'account')] should really be [.='account']. I'm not sure of that, but using text() instead of ".", and using contains() instead of "=", are both common mistakes.

xpath not working in java

I have the below xml string
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<Structure>
<LongUnsigned value="142794"/>
<OctetString value="07E2051E030F1E0404800000"/>
<Structure>
<OctetString value="07E2051E030F1E0404800000"/>
<OctetString value="66574536387"/>
<Array>
<Structure><OctetString value="0000000000000001"/><OctetString value="9889892347"/></Structure>
<Structure><OctetString value="00098347586768574"/><OctetString value="6283046502"/></Structure>
<Structure><OctetString value="0000011000000001"/><OctetString value="899734729847586"/></Structure>
</Array>
</Structure>
</Structure>
I am using the below xpath but it always returns an empty string.
XPath xPath = XPathFactory.newInstance().newXPath();
try {
String eval = xPath.evaluate("//Structure/Structure/Array", new InputSource(new StringReader(xmlString)));
System.out.println("Eval:" + eval);
} catch (XPathExpressionException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
I tried running this xpath online and it seems to work just fine. What am i missing in Java that makes it not work as expected.
Your XPath expression selects an element node, not a string. So you need to ask for the result to be returned as a NODESET.
I'm not familiar with Java reading XML but your XPath should be something like this:
/Structure/Structure/Array/Stucture/OctetString/#value
This will start at the root-node <Structure>, move down to the nested <Structure>, further down to <Array>, then to the nested <OctetString> elements to fetch their value attribute.
Your expression //Structure/Structure/Array starts at any <Structure> (due to the //) and tries to read the value of <Array>, but there is no value, just deeper nodes...

get child nodes from parent (xml, java)

UPDATE
i was specifically targeting staff under some root node, not all "staff" elements in the whole document. i forgot to mention this important detail in the question. sorry guys.
i found this answer to my question:
getElementsByTagName
But with this data:
<one>
<two>
<three>
<company>
<staff id="1001">
<firstname>Golf</firstname>
<lastname>4</lastname>
<nickname>Schnecke</nickname>
<salary>1</salary>
</staff>
<staff id="2001">
<firstname>Audi</firstname>
<lastname>R8</lastname>
<nickname>Rennaudi</nickname>
<salary>1111111</salary>
</staff>
<staff id="2002">
<firstname>Skoda</firstname>
<lastname>xyz</lastname>
<nickname>xyz</nickname>
<salary>0.1</salary>
</staff>
</company>
</three>
</two>
</one>
and this code:
public static void parseXML2() {
File fXmlFile = new File("src\\main\\java\\staff.xml");
DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder dBuilder = null;
try {
dBuilder = dbFactory.newDocumentBuilder();
} catch (ParserConfigurationException ex) {
Logger.getLogger(MyParser.class.getName()).log(Level.SEVERE, null, ex);
}
Document doc = null;
try {
doc = dBuilder.parse(fXmlFile);
} catch (SAXException ex) {
Logger.getLogger(MyParser.class.getName()).log(Level.SEVERE, null, ex);
} catch (IOException ex) {
Logger.getLogger(MyParser.class.getName()).log(Level.SEVERE, null, ex);
}
System.out.println("test");
System.out.println(doc.getElementsByTagName("company").item(0).getTextContent());
}
i dont get just one staff element, but all of them. how come?
i was expecting to get:
Golf
4
Schnecke
1
but instead i get this:
Golf
4
Schnecke
1
Audi
R8
Rennaudi
1111111
Skoda
xyz
xyz
0.1
looks like your post is mostly code, please add more details...yes the details are there.
You are almost there. If you want to get the text contents of the first staff node, then get the elements by that tag name:
System.out.println(doc.getElementsByTagName("staff").item(0).getTextContent());
// ^^^^^^
Update
In case you want to get the first staff node under company, then you can find them with node type and node name checks. Here is a rudimentary loop to do this:
Node companyNode = doc.getElementsByTagName("company").item(0);
NodeList companyChildNodes = companyNode.getChildNodes();
for (int i = 0; i < companyChildNodes.getLength(); i++) {
Node node = companyChildNodes.item(i);
if (node.getNodeType() == Node.ELEMENT_NODE && Objects.equals("staff", node.getNodeName())) {
System.out.println(node.getTextContent());
break;
}
}
You might want to refactor the for loop into a separate method.
You can use XPATH too. I think it's more concise:
XPathFactory xPathfactory = XPathFactory.newInstance();
XPath xpath = xPathfactory.newXPath();
XPathExpression expr = xpath.compile("//company/staff[1]");
NodeList nl = (NodeList) expr.evaluate(doc, XPathConstants.NODESET);
System.out.println(nl.item(0).getTextContent());
Explanation:
//company selects all the company tags, regardless where they are in the xml. It's the // that ignores the rest of the xml structure.
//company/staff selects all the staff tags that are under company tag.
[0] selects the first such item.
You have a line of code which gets the text content from the first company.
System.out.println(doc.getElementsByTagName("company").item(0).getTextContent());
This gets all the text content in the first company node which in this case is the data inside 3 staff elements. If you want to select the first staff element, you can either select it by name or by getting the first child of the company.
If your xml has a format like you mentioned, and with this line of code,
System.out.println(doc.getElementsByTagName("company").item(0).getTextContent();
you are printing ALL contents under the COMPANY tag name, thus its gonna print EVERYTHING in company, if you want to select to print only 1st staff, change tagname in the sysout to "staff" and item variable to (0,1,2,3 -> index of wanted staff)
System.out.println(doc.getElementsByTagName("staff").item(0).getTextContent();
i havent tried this out, but i think its gonna work

Extracting the node values in XML with XPath in Java

I have an XML document:
<response>
<result>
<phone>1233</phone>
<sys_id>asweyu4</sys_id>
<link>rft45fgd</link>
<!-- Many more in result -->
</result>
<!-- Many more result nodes -->
</response>
The XML structure is unknown. I am getting XPath for attributes from user.
e.g. inputs are strings like:
//response/result/sys_id , //response/result/phone
How can I get these node values for whole XML document by evaluating XPath?
I referred this but my xpath is as shown above i.e it does not have * or text() format.
The xpath evaluator works perfectly fine with my input format, so is there any way I can achieve the same in java?
Thank you!
It's difficult without seeing your code... I'd just evaluate as a NodeList and then call getTextContent() on each node in the result list...
String input = "<response><result><phone>1233</phone><sys_id>asweyu4</sys_id><link>rft45fgd</link></result><result><phone>1233</phone><sys_id>another-sysid</sys_id><link>another-link</link></result></response>";
Document doc = DocumentBuilderFactory.newInstance().newDocumentBuilder()
.parse(new ByteArrayInputStream(input.getBytes("UTF-8")));
XPath path = XPathFactory.newInstance().newXPath();
NodeList node = (NodeList) path.compile("//response/result/sys_id").evaluate(doc, XPathConstants.NODESET);
for (int i = 0; i < node.getLength(); i++) {
System.out.println(node.item(i).getTextContent());
}
Output
asweyu4
another-sysid

Read element inside element from XML in SAX or Dom

<rootNode>
<Movies>
<Movie id=1>
<title> title1</title>
<Actors>
<Actor>Actor1</Actor>
<Actor>Actor2</Actor>
<Actors>
</Movie>
</Movies>
<performers >
<performer id=100>
<name>name1</name>
<movie idref=1/>
</performer>
</performers>
</rootNode>
Question1: I only want to get the movie under the movies. I tried both of DOM and SAX. It also returns the under performers. How can I avoid this by using SAX or DOM
DOM:
doc.getElementsByTagName("movie");
SAX:
public void startElement(String uri, String localName,String qName,
Attributes attributes) throws SAXException {
if (qName.equalsIgnoreCase("movie"))
Question2: How can I get the element inside element (Actor under movies) by using DOM or SAX?
Basically, what I want to do is output the data in order.
1,title, Actor1,Actor2
100,name1,1
doc.getElementsByTagName("movies")[0].childNodes;
gets you all the movies/movie nodes (watch for lower-/upper-case!). See here http://www.w3schools.com/dom/dom_intro.asp for a short tutorial.
XPath is designed for this type of extraction. For your example file, the query would be something like the following. For simplicity, I assumed your xml was in a res/raw, but in practice you will need to create the InputSource from where ever you are getting your xml.
XPath xpath = XPathFactory.newInstance().newXPath();
String expression = "/rootNode/Movies/Movie";
try {
NodeList nodes = (NodeList) xpath.evaluate(expression, doc,XPathConstants.NODESET);
} catch (XPathExpressionException e) {
e.printStackTrace();
}

Categories

Resources