how to retrieve XML data using XPath which contains namespace in Java? - java

i know there are plenty of this topic in this page but sadly, i still cant get my solution..
here is my xml code:
<?xml version="1.0" encoding="UTF-8"?>
<ns1:Request xmlns:ns1="http://www.sea.com">
<ns1:PayrollRequest>
<ns1:PayrollCost>
<ns1:PayrollID>123</ns1:PayrollID>
<ns1:BatchID>7770</ns1:BatchID>
<ns1:CompanyId>001</ns1:CompanyId>
<ns1:GrossPay>60000</ns1:GrossPay>
</ns1:PayrollCost>
</ns1:PayrollRequest>
</ns1:Request>
and this is my code in java:
import org.w3c.dom.*;
import javax.xml.xpath.*;
import javax.xml.parsers.*;
import java.io.IOException;
import org.xml.sax.SAXException;
public class XPathTry {
public static void main(String[] args)
throws ParserConfigurationException, SAXException,
IOException, XPathExpressionException {
DocumentBuilderFactory domFactory =
DocumentBuilderFactory.newInstance();
domFactory.setNamespaceAware(true);
DocumentBuilder builder = domFactory.newDocumentBuilder();
Document doc = builder.parse("SamplePayroll2.xml");
XPath xpath = XPathFactory.newInstance().newXPath();
// display all
XPathExpression expr = xpath.compile("//PayrollCost/*/text()");
Object result = expr.evaluate(doc, XPathConstants.NODESET);
NodeList nodes = (NodeList) result;
for (int i = 0; i < nodes.getLength(); i++) {
System.out.println(nodes.item(i).getNodeValue());
}
}
}
ya, as usual, i cant get the output as it only displays:
Process exited with exit code 0.
the output will only display when i remove the ns:1 which the code for the xml will be like this:
<?xml version="1.0" encoding="UTF-8"?>
<Request xmlns:ns1="http://www.sea.com">
<PayrollRequest>
<PayrollCost>
<PayrollID>123</PayrollID>
<BatchID>7770</BatchID>
<CompanyId>001</CompanyId>
<GrossPay>60000</GrossPay>
</PayrollCost>
</PayrollRequest>
</Request>
The problem is, all the suggestions i found in the net none seems to be working:
for example, i already tried the
/*/namespace::*[name()='']
//*[local-name() = 'Element' and namespace-uri() = namespace-uri(/*)]
/*[local-name()=' ']/*[local-name()=' ']/*[local-name()=' ']
etc2..
the only best output i can get is, it will display:
null
can anyone give me the correct code for my problem?
Thanks in Advance!

You are going to have to create a subclass of javax.xml.namespace.NamespaceContext and set it on xpath:
xpath.setNamespaceContext(new NamespaceContext() {
#SuppressWarnings("rawtypes")
#Override
public Iterator getPrefixes(final String namespaceURI) {
return Collections.singleton("ns1").iterator();
}
#Override
public String getPrefix(final String namespaceURI) {
return "ns1";
}
#Override
public String getNamespaceURI(final String prefix) {
return "http://www.sea.com";
}
});
Then you can add the namespace prefix to the XPath expression:
XPathExpression expr = xpath.compile("//ns1:PayrollCost/*/text()");

You need to use a NamespaceContext for your XPath expression. You can read more about how to do this here

Related

Getting all attributes of an xml by using java and Xpath

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

Java Xpath multiple elements with same name of a parent node

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

Xpath and condition

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);
}

Multiple NameSpace in Xml Xpath value

Am new in using Xpath parsing in Java for Xmls. But I learnt it and it worked pretty well until this below issue am not sure how to go traverse to next node in this . Please find the below code and Let me know what needs to be corrected .
package test;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.TransformerException;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;
import org.w3c.dom.Document;
import org.w3c.dom.NodeList;
public class CallTestcall {
public static void main(String[] args) throws Exception {
DocumentBuilderFactory domFactory = DocumentBuilderFactory.newInstance();
domFactory.setNamespaceAware(true);
DocumentBuilder builder = domFactory.newDocumentBuilder();
String responsePath1 = "C:/Verizon/webserviceTestTool/generatedResponse/example.xml";
Document doc1 = builder.parse(responsePath1);
String responsePath0 = "C:/Verizon/webserviceTestTool/generatedResponse/response.xml";
Document doc0 = builder.parse(responsePath0);
example0(doc0);
example1(doc1);
}
private static void example0(Document example)
throws XPathExpressionException, TransformerException {
System.out.println("\n*** First example - namespacelookup hardcoded ***");
XPath xPath = XPathFactory.newInstance().newXPath();
xPath.setNamespaceContext(new HardcodedNamespaceResolver());
String result = xPath.evaluate("s:Envelope/s:Body/ns1:UpdateSessionResponse",
example);
// I tried all the Values to traverse further to UpdateSessionResult but am not able to I used the following xpath expressions
result = xPath.evaluate("s:Envelope/s:Body/ns1:UpdateSessionResponse/a:UpdateSessionResult",
example);
result = xPath.evaluate("s:Envelope/s:Body/ns1:UpdateSessionResponse/i:UpdateSessionResult",
example);
System.out.println("example0 : "+result);
}
private static void example1(Document example)
throws XPathExpressionException, TransformerException {
System.out.println("\n*** First example - namespacelookup hardcoded ***");
XPath xPath = XPathFactory.newInstance().newXPath();
xPath.setNamespaceContext(new HardcodedNamespaceResolver());
String result = xPath.evaluate("books:booklist/technical:book/:author",
example);
System.out.println("example1 : "+result);
}
}
Please find the class that implements nameSpaceContext where I have added the prefixes
package test;
import java.util.Iterator;
import javax.xml.XMLConstants;
import javax.xml.namespace.NamespaceContext;
public class HardcodedNamespaceResolver implements NamespaceContext {
/**
* This method returns the uri for all prefixes needed. Wherever possible it
* uses XMLConstants.
*
* #param prefix
* #return uri
*/
public String getNamespaceURI(String prefix) {
if (prefix == null) {
throw new IllegalArgumentException("No prefix provided!");
} else if (prefix.equals(XMLConstants.DEFAULT_NS_PREFIX)) {
return "http://univNaSpResolver/book";
} else if (prefix.equals("books")) {
return "http://univNaSpResolver/booklist";
} else if (prefix.equals("fiction")) {
return "http://univNaSpResolver/fictionbook";
} else if (prefix.equals("technical")) {
return "http://univNaSpResolver/sciencebook";
} else if (prefix.equals("s")) {
return "http://schemas.xmlsoap.org/soap/envelope/";
} else if (prefix.equals("a")) {
return "http://channelsales.corp.cox.com/vzw/v1/data/";
} else if (prefix.equals("i")) {
return "http://www.w3.org/2001/XMLSchema-instance";
} else if (prefix.equals("ns1")) {
return "http://channelsales.corp.cox.com/vzw/v1/";
}
else {
return XMLConstants.NULL_NS_URI;
}
}
public String getPrefix(String namespaceURI) {
// Not needed in this context.
return null;
}
public Iterator getPrefixes(String namespaceURI) {
// Not needed in this context.
return null;
}
}
Please find my Xml ::::
String XmlString = "<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/"><s:Body><UpdateSessionResponse xmlns="http://channelsales.corp.cox.com/vzw/v1/"><UpdateSessionResult xmlns:a="http://channelsales.corp.cox.com/vzw/v1/data/" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<a:ResponseHeader>
<a:SuccessFlag>true</a:SuccessFlag>
<a:ErrorCode i:nil="true"/>
<a:ErrorMessage i:nil="true"/>
<a:Timestamp>2012-12-05T15:28:35.5363903-05:00</a:Timestamp>
</a:ResponseHeader>
<a:SessionId>cd3ce09e-eb33-48e8-b628-ecd406698aee</a:SessionId>
<a:CacheKey i:nil="true"/>
Try the following. It works for me.
result = xPath.evaluate("/s:Envelope/s:Body/ns1:UpdateSessionResponse/ns1:UpdateSessionResult",
example);
Since you are searching from the root of the document, precede the xpath expression with a forward slash (/)
Also, in the XML fragment below, the string xmlns="http... means you are setting that to be the default namespace. In your namespace resolver you are giving this the prefix ns1. So even though UpdateSessionResult is defining two namespace prefixes a and i, it does not use those prefixes itself (for example <a:UpdateSessionResult...) therefore it belongs to the default namespace (named 'ns1')
<UpdateSessionResponse xmlns="http://channelsales.corp.cox.com/vzw/v1/">
<UpdateSessionResult xmlns:a="http://channelsales.corp.cox.com/vzw/v1/data/" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
That's why you need to use ns1:UpdateSessionResult instead of either a:UpdateSessionResult or i:UpdateSessionResult

How to use XPath on xml docs having default namespace

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

Categories

Resources