I have the following xml:
<?xml version="1.0" encoding="UTF-8"?>
<root xmlns="http://www.test.com/rest/v1" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<child test="folder" id="2019-05-15-04.52.05.641880A01" />
<child test="folder" id="2019-05-15-04.52.05.901880A02" />
</root>
I want to read the above xml by using Java code and Xpath, retrieve the id's of the child nodes(i.e. id="2019-05-15-04.52.05.641880A01" and id="2019-05-15-04.52.05.901880A02") and store them into List. I tried with the following java code:
InputSource source = new InputSource(new StringReader(xml));
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder db = dbf.newDocumentBuilder();
org.w3c.dom.Document document = db.parse(source);
XPathFactory xpathFactory = XPathFactory.newInstance();
XPath xpath = xpathFactory.newXPath();
return xpath.evaluate(expression, document);
I called the above the above method with the following Xpath along with the input xml:
*[local-name()='root']/*[local-name()='child']/#id
But I am getting only one id, not all the id's. Any idea on how to get all the id's?
I think your Xpath is right. You can verify it with the following test class.
package com.idsk.commons.xsl;
import java.io.IOException;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpression;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;
import org.w3c.dom.Document;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
public class Test {
public static void main(String[] args) throws ParserConfigurationException, SAXException, IOException, XPathExpressionException {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setNamespaceAware(true); // never forget this!
DocumentBuilder builder = factory.newDocumentBuilder();
Document doc = builder.parse("D://NewFile.xml");
// Create XPath
XPathFactory xpathfactory = XPathFactory.newInstance();
XPath xpath = xpathfactory.newXPath();
XPathExpression expr = xpath.compile("*[local-name()='root']/*[local-name()='child']/#id");
Object result = expr.evaluate(doc, XPathConstants.NODESET);
NodeList nodes = (NodeList) result;
List<String> ids = new ArrayList<>();
for (int i = 0; i < nodes.getLength(); i++) {
System.out.println(nodes.item(i).getNodeValue());
ids.add(nodes.item(i).getNodeValue()); //store them into List
}
}
}
It will create following output:
2019-05-15-04.52.05.641880A01
2019-05-15-04.52.05.901880A02
Related
This question already has answers here:
XPath with namespace in Java
(2 answers)
Closed 4 years ago.
Xpath seems to not work. I've already tried a few things, but nothing seems to work. What am I doing wrong?
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setNamespaceAware(true);
DocumentBuilder builder = factory.newDocumentBuilder();
Document doc = builder.parse(new InputSource(new StringReader(result)));
XPathFactory xPathfactory = XPathFactory.newInstance();
XPath xpath = xPathfactory.newXPath();
XPathExpression expr = xpath.compile("//cm:URL/#value");
String msg = expr.evaluate(doc, XPathConstants.STRING).toString();
logger.debug(msg);
The XML I have looks like this:
<ItemXML xmlns="http://www.ibm.com/xmlns/db2/cm/beans/1.0/schema" xmlns:ns2="http://www.ibm.com/xmlns/db2/cm/api/1.0/schema">
<DOCUMENTS SCA_DATE="#" SCA_NR="#" cm:PID="#" xmlns:cm="http://www.ibm.com/xmlns/db2/cm/api/1.0/schema" xmlns:ns1="http://www.ibm.com/xmlns/db2/cm/beans/1.0/schema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<cm:properties type="#">
<cm:lastChangeUserid value="#"/>
<cm:lastChangeTime value="#"/>
<cm:createUserid value="#"/>
<cm:createTime value="#"/>
<cm:semanticType value="#"/>
<cm:ACL name="#"/>
<cm:lastOperation name="#" value="#"/>
</cm:properties>
<ns1:CONTRACTS AM="#"/>
<ns1:BASE cm:PID="#" cm:partNumber="#">
<cm:properties type="item" xsi:type="#">
<cm:lastChangeUserid value="#"/>
<cm:lastChangeTime value="#"/>
<cm:createUserid value="#"/>
<cm:createTime value="#"/>
<cm:semanticType value="#"/>
<cm:ACL name="#"/>
<cm:lastOperation name="#" value="#"/>
</cm:properties>
<cm:resourceObject MIMEType="application/pdf" RMName="#" SMSCollName="#" externalObjectName="" originalFileName="#" resourceFlag="#" resourceName="" size="#">
<cm:URL value="https://testurl.com"/>
</cm:resourceObject>
</ns1:BASE>
</DOCUMENTS>
</ItemXML>
I want the value of cm:URL --> https://testurl.com saved as String.
Important: Xpath should find the value regardless of the xml structure.
Without using XPath you can do:
NodeList elementsByTagName = doc.getDocumentElement().getElementsByTagName("cm:URL");
System.out.println("result: " + elementsByTagName.item(0).getAttributes().getNamedItem("value").getNodeValue());
There is issue in following line :-
Document doc = builder.parse(new InputSource(new StringReader(result)));
i tried to print the document, it is giving me output as [#document: null], can you first try to print the document in console and let me know if it is not null.
as suggested by #bangnab use his solution, i tried its working
import java.io.File;
import java.io.IOException;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.xpath.XPathExpressionException;
import org.w3c.dom.Document;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
public class XpathTester {
public static void main(String[] args) throws XPathExpressionException, ParserConfigurationException, SAXException, IOException {
File inputFile = new File("C:\\Users\\Arvind.Carpenter\\Desktop\\input.txt");
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setNamespaceAware(true);
DocumentBuilder builder = factory.newDocumentBuilder();
Document doc = builder.parse(inputFile);
doc.getDocumentElement().normalize();
NodeList elementsByTagName = doc.getDocumentElement().getElementsByTagName("cm:URL");
//you can iterate over this node list and get all the URL i am printing first one
System.out.println("result: " + elementsByTagName.item(0).getAttributes().getNamedItem("value").getNodeValue());
}
}
I need to get the total number of nodes in an xml file in java ( java dom ...)
In the file below, the total number of elements is 15
<?xml version="1.0" encoding="UTF-8"?>
<personnes>
<etudiant classe="P2">
<nom>CynO</nom>
<prenoms>
<prenom>Nicolas</prenom>
<prenom>Laurent</prenom>
</prenoms>
<age>25</age>
</etudiant>
<etudiant classe="P1">
<nom>Superwoman</nom>
<prenoms>
<prenom>Sia</prenom>
</prenoms>
<age>34</age>
</etudiant>
<etudiant classe="P3">
<nom>Don Corleone</nom>
<age>28</age>
</etudiant>
</personnes>
thank you
Try with following code snippet. It is worked for me.
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import org.w3c.dom.Document;
import org.w3c.dom.NodeList;
public class CountNoOfElements{
public static void main(String args[]) throws Exception {
String filepath = "test.xml";
DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder docBuilder = docFactory.newDocumentBuilder();
Document doc = docBuilder.parse(filepath);
NodeList nodeList = doc.getElementsByTagName("*");
int count = nodeList.getLength();
System.out.println("Total of elements : " + count);
}
}
I have an xml like below.
<name>
<value>123</value>
<value>456</value>
<value>789</value>
</name>
Now using java's Xpath query I tried below method
NodeList list3 = (NodeList) xpath.evaluate("name/value", element,XPathConstants.NODESET);
But it gives me only first value, how can I print all <value> tags ?
Your XPath expression is correct, there is most likely another problem in your code. You really should provide a complete example which demonstrates your problem.
The following code demonstrates how this would look like:
import java.io.StringReader;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
public class XmlTest {
public static void main(String[] args) throws Exception {
String xml = "<name>\n" +
"<value>123</value>\n" +
"<value>456</value>\n" +
"<value>789</value>\n" +
"</name>";
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
Document doc = builder.parse(new InputSource(new StringReader(xml)));
XPathFactory xPathfactory = XPathFactory.newInstance();
XPath xpath = xPathfactory.newXPath();
NodeList list = (NodeList) xpath.evaluate("name/value", doc, XPathConstants.NODESET);
for (int i = 0; i < list.getLength(); ++i) {
Node node = list.item(i);
System.out.println(node.getNodeName());
}
}
}
Running this results in the following output:
value
value
value
How can i get values of nodes if two nodes are is present under parent node.
Eg: I have the following Xml.
<?xml version="1.0" encoding="UTF-8"?>
<Services xmlns="http://sample.schema.com/abc">
<service>
<name>Sample</name>
<uri>/v9.0/sample.123.com
</uri>
</service>
<service>
<name>Sample 2</name>
</service>
<service>
<name>Sample 3</name>
<uri>/v9.0/sample3.123.com
</uri>
</service>
<service>
<name>Sample 4</name>
<uri>/v9.0/sample4.123.com
</uri>
</service>
<service>
<name>Sample 5</name>
<uri>/v9.0/sample5.123.com
</uri>
</service>
<service>
<name>Sample 6</name>
</service>
<service>
<name>Sample 7</name>
<uri>/v9.0/sample7.123.com
</uri>
</service>
<service>
<name>Sample 8</name>
</service>
</Services>
My Code:
import java.io.IOException;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpression;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;
import org.w3c.dom.Document;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
public class SimpleXpath {
public static void main(String[] args) throws ParserConfigurationException,
SAXException, IOException, XPathExpressionException {
DocumentBuilderFactory domFactory = DocumentBuilderFactory.newInstance();
domFactory.setNamespaceAware(false); // never forget this!
DocumentBuilder builder = domFactory.newDocumentBuilder();
Document doc = builder.parse("testService1.xml");
XPathFactory factory = XPathFactory.newInstance();
XPath xpath = factory.newXPath();
XPathExpression expr = xpath.compile("//Services/service[(name) and (uri)/text()]");
Object result = expr.evaluate(doc, XPathConstants.NODESET);
NodeList nodes = (NodeList) result;
for (int i = 0; i < nodes.getLength(); i++) {
String value=nodes.item(i).getNodeValue();
System.out.println(" output : "+i+" "+value);
}
}
}
I want to read the values of name and url from th above xml, If the name and uri is present under the service node.
We can see that some of the service node only contains the name. I want to avoid those one. My xpath expression gives null value as output.
How can i get the "text" of name and uri if service contains both?
Can i get the output as name as first and uri as second using an xpath(If both present under service)?
Thnanks a lot.
John
First of all, you could just use simpler xpath: //Services/service[name and uri]. Then NodeList returns you a list of service elements. You could iterate other its children and find elements named name and uri, then take their text values. Or you could create two more xpath expressions like this:
XPathExpression exprName = xpath.compile("normalize-space(name)");
String name = (String)exprName.evaluate(serviceNode, XPathConstants.STRING);
and the same for uri.
Thank you all....
The following code fulfills the requirement.
If there any other option to improve the quality please suggest.
public static void main(String[] args) throws ParserConfigurationException,
SAXException, IOException, XPathExpressionException {
DocumentBuilderFactory domFactory = DocumentBuilderFactory.newInstance();
domFactory.setNamespaceAware(false); // never forget this!
DocumentBuilder builder = domFactory.newDocumentBuilder();
Document doc = builder.parse("testService1.xml");
XPathFactory factory = XPathFactory.newInstance();
XPath xpath = factory.newXPath();
XPathExpression expr = xpath.compile("//Services/service[(name) and (uri)]/name/text()");
Object result = expr.evaluate(doc, XPathConstants.NODESET);
NodeList nodes = (NodeList) result;
XPathExpression expr1 = xpath.compile("//Services/service[(name) and (uri)]/uri/text()");
Object result1 = expr1.evaluate(doc, XPathConstants.NODESET);
NodeList nodes1 = (NodeList) result1;
for (int i = 0; i < nodes1.getLength()&&i < nodes.getLength(); i++) {
String value=nodes.item(i).getNodeValue();
String value1=nodes1.item(i).getNodeValue();
System.out.println(" output : "+i+" "+value+ " "+ value1);
}
}
This xpath select elements that has 2 children
//*[count(*)=2]
for(int j=0; j<children.getLength();j++) {
if (children.item(j) instanceof Element == false)
continue;
NamedNodeMap n = children.item(j).getAttributes();
passwordTagAttr=(Attr) n.getNamedItem("name");
passwordTagAttr=(Attr) n.getNamedItem("uri");
passwordTag=stopTagAttr.getValue();
passwordList.add(passwordTag);
}
I want to manipulate xml doc having default namespace but no prefix. Is there a way to use xpath without namespace uri just as if there is no namespace?
I believe it should be possible if we set namespaceAware property of documentBuilderFactory to false. But in my case it is not working.
Is my understanding is incorrect or I am doing some mistake in code?
Here is my code:
DocumentBuilderFactory domFactory = DocumentBuilderFactory.newInstance();
domFactory.setNamespaceAware(false);
try {
DocumentBuilder builder = domFactory.newDocumentBuilder();
Document dDoc = builder.parse("E:/test.xml");
XPath xPath = XPathFactory.newInstance().newXPath();
NodeList nl = (NodeList) xPath.evaluate("//author", dDoc, XPathConstants.NODESET);
System.out.println(nl.getLength());
} catch (Exception e) {
e.printStackTrace();
}
Here is my xml:
<?xml version="1.0" encoding="UTF-8"?>
<root xmlns="http://www.mydomain.com/schema">
<author>
<book title="t1"/>
<book title="t2"/>
</author>
</root>
The XPath processing for a document that uses the default namespace (no prefix) is the same as the XPath processing for a document that uses prefixes:
For namespace qualified documents you can use a NamespaceContext when you execute the XPath. You will need to prefix the fragments in the XPath to match the NamespaceContext. The prefixes you use do not need to match the prefixes used in the document.
http://download.oracle.com/javase/6/docs/api/javax/xml/namespace/NamespaceContext.html
Here is how it looks with your code:
import java.util.Iterator;
import javax.xml.namespace.NamespaceContext;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathFactory;
import org.w3c.dom.Document;
import org.w3c.dom.NodeList;
public class Demo {
public static void main(String[] args) {
DocumentBuilderFactory domFactory = DocumentBuilderFactory.newInstance();
domFactory.setNamespaceAware(true);
try {
DocumentBuilder builder = domFactory.newDocumentBuilder();
Document dDoc = builder.parse("E:/test.xml");
XPath xPath = XPathFactory.newInstance().newXPath();
xPath.setNamespaceContext(new MyNamespaceContext());
NodeList nl = (NodeList) xPath.evaluate("/ns:root/ns:author", dDoc, XPathConstants.NODESET);
System.out.println(nl.getLength());
} catch (Exception e) {
e.printStackTrace();
}
}
private static class MyNamespaceContext implements NamespaceContext {
public String getNamespaceURI(String prefix) {
if("ns".equals(prefix)) {
return "http://www.mydomain.com/schema";
}
return null;
}
public String getPrefix(String namespaceURI) {
return null;
}
public Iterator getPrefixes(String namespaceURI) {
return null;
}
}
}
Note:
I also used the corrected XPath suggested by Dennis.
The following also appears to work, and is closer to your original question:
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathFactory;
import org.w3c.dom.Document;
import org.w3c.dom.NodeList;
public class Demo {
public static void main(String[] args) {
DocumentBuilderFactory domFactory = DocumentBuilderFactory.newInstance();
try {
DocumentBuilder builder = domFactory.newDocumentBuilder();
Document dDoc = builder.parse("E:/test.xml");
XPath xPath = XPathFactory.newInstance().newXPath();
NodeList nl = (NodeList) xPath.evaluate("/root/author", dDoc, XPathConstants.NODESET);
System.out.println(nl.getLength());
} catch (Exception e) {
e.printStackTrace();
}
}
}
Blaise Doughan is right, attached code is correct.
Problem was somewhere elese. I was running all my tests through Application launcher in Eclipse IDE and nothing was working. Then I discovered Eclipse project was cause of all grief. I ran my class from command prompt, it worked. Created a new eclipse project and pasted same code there, it worked there too.
Thank you all guys for your time and efforts.
I've written a simple NamespaceContext implementation (here), that might be of help. It takes a Map<String, String> as input, where the key is a prefix, and the value is a namespace.
It follows the NamespaceContext spesification, and you can see how it works in the unit tests.
Map<String, String> mappings = new HashMap<>();
mappings.put("foo", "http://foo");
mappings.put("foo2", "http://foo");
mappings.put("bar", "http://bar");
context = new SimpleNamespaceContext(mappings);
context.getNamespaceURI("foo"); // "http://foo"
context.getPrefix("http://foo"); // "foo" or "foo2"
context.getPrefixes("http://foo"); // ["foo", "foo2"]
Note that it has a dependency on Google Guava