How to use XPath on xml docs having default namespace - java

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

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

Source Attachment does not contain the source for the file XPathImpl.class

I searched and could not find this particular issue discussed. I installed eclipse and selenium, and want a way to work with XML. I found several solutions, but the all seem mostly to use a source file that I don't have? I tried downloading jaxb-xjc-2.1.13.jar, which I found in an internet search, but perhaps I don't know how to point it to my code. For this code:
package automationFramework;
import java.io.*;
import javax.xml.parsers.*;
import org.w3c.dom.*;
import javax.xml.xpath.*;
then I try to parse an xml:
public static String getElementById(String id) {
try {
DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = dbFactory.newDocumentBuilder();
Document doc = builder.parse(new File("somelocation"));
XPathFactory xpFactory = XPathFactory.newInstance();
XPath xpath = xpFactory.newXPath();
String pwdout = xpath.evaluate("/user[#id='" + id.toUpperCase() + "']/cZAR", doc);
return pwdout;
} catch (Exception e) {
return null;
}
}
etc. When it tries to evaluate the "xpath.evaluate...." line, it throws a "Source Attachment does not contain the source for the file XPathImpl.class"

How to use name() and/or node() in Java XPath query?

I have some XML that roughly looks like this:
<project type="mankind">
<suggestion>Build the enterprise</suggestion>
<suggestion>Learn Esperanto</suggestion>
<problem>Solve world hunger</suggestion>
<discussion>Do Vulcans exist</discussion>
</project>
I want to use XPath to find out the names of the second level elements (there can be elements I won't know upfront) using Java. This is the code I tried:
public NodeList xpath2NodeList(Document doc, String xPathString) throws XPathExpressionException {
XPath xpath = XPathFactory.newInstance().newXPath();
MagicNamespaceContext nsc = new MagicNamespaceContext();
xpath.setNamespaceContext(nsc);
Object exprResult = xpath.evaluate(xPathString, doc, XPathConstants.NODESET);
return (NodeList) exprResult;
}
My XPath is /project/*/name(). I get the error:
javax.xml.transform.TransformerException: Unknown nodetype: name
A query like /project/suggestion works as expected. What am I missing? I'd like to get a list with the tag names.
Java6 (don't ask).
I think your implementation only supports XPath 1.0. If that were true, only the following would work:
"name(/project/*)"
The reason for this is that in the XPath 1.0 model, you cannot use functions (like name()) as a step in a path expression. Your code throws an exception and in this case, the processor mistakes your function name() for an unknown node test (like comment()). But there is nothing wrong with using a path expression as the argument of the name() function.
Unfortunately, if an XPath 1.0 function that can only handle a single node as an argument is given a sequence of nodes, only the first one is used. Therefore, it is likely that you will only get the first element name as a result.
XPath 1.0's capability to manipulate is very limited and often the easiest way to get around such problems is to reach for the higher-level language that uses XPath as the query language (in your case Java). Or put another way: Write an XPath expression to retrieve all relevant nodes and iterate over the result, returning the element names, in Java.
With XPath 2.0, your inital query would be fine. Also see this related question.
Below code may answer your original question.
package com.example.xpath;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
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.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
public class XPathReader {
static XPath xPath = XPathFactory.newInstance().newXPath();
public static void main(String[] args) {
try {
FileInputStream file = new FileInputStream(new File("c:/mankind.xml"));
DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = builderFactory.newDocumentBuilder();
Document xmlDocument = builder.parse(file);
XPathExpression expr = xPath.compile("//project/*");
NodeList list= (NodeList) expr.evaluate(xmlDocument, XPathConstants.NODESET);
for (int i = 0; i < list.getLength(); i++) {
Node node = list.item(i);
System.out.println(node.getNodeName() + "=" + node.getTextContent());
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (SAXException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (ParserConfigurationException e) {
e.printStackTrace();
} catch (XPathExpressionException e) {
e.printStackTrace();
}
}
}
Assuming the input is corrected (Solve world hunger), the code should print:
suggestion=Build the enterprise
suggestion=Learn Esperanto
problem=Solve world hunger
discussion=Do Vulcans exist

how to retrieve XML data using XPath which contains namespace in 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

Categories

Resources