XPath compiling behaviour - java

I am testing my application and realised that behaviour is different when compiling.
For example, if my expression to compile is :
XPathExpression expr = xPath.compile("/DocDetails/TransactionSignature");
And :
XPathExpression expr2 = xPath.compile("/DocDetails/" + x);
x is declared as a String datatype.
Lets say that x in expr2 is "abc", XPathExpression is compiled with no issues.
But if x in expr2 is "123abc" OR "123", XPathExpression throws a :
javax.xml.transform.TransformerException: A location step was expected
following the '/' or '//' token.
Just curious regarding this behaviour..
Here is the full code for reference:
String document = "C:/Users/Eunice/Documents/MITS/doc.xml";
String document2 = "C:/Users/Eunice/Documents/MITS/doc2.xml";
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
Document doc = builder.parse(document);
Document doc2 = builder.parse(document2);
XPathFactory xPathFactory = XPathFactory.newInstance();
XPath xPath = xPathFactory.newXPath();
XPathExpression expr = xPath.compile("/DocDetails/TransactionSignature");
Node node = (Node)expr.evaluate(doc, XPathConstants.NODE);
String x = node.getTextContent();
System.out.println(x);
XPathExpression expr2 = xPath.compile("/DocDetails/" + x);
Node node2 = (Node)expr2.evaluate(doc2, XPathConstants.NODE);
if (node2 == null)
System.out.println("null");
else
System.out.println("not null " + node2.getTextContent());
And this is the XML file:
<DocDetails>
<TransactionSignature>abc123</TransactionSignature>
</DocDetails>

But if x in expr2 is "123abc" OR "123", XPathExpression throws a
XML element name cannot start with number. Hence your example is equivalent to
XPathExpression expr2 = xPath.compile("/DocDetails/123abc");
I guess XPath parser does not expect it.
You should also provide full XML. I believe it certainly does not contain anything like <DocDetails><TransactionSignature>abc123</TransactionSignature><123abc>something</123abc></DocDetails>. This is simply invalid XML.

I finally found the answer after much searching!
It is actually illegal to start an element tag with numbers.
As can be seen in this stackoverflow answer
Originally, this line was throwing an transformer exception:
XPathExpression expr2 = xPath.compile("/DocDetails/" + x);
Since it is illegal to start with numbers, they are probably reading it as an invalid tag.
Which means this line is actually reading "/DocDetails/" instead of "/DocDetails/123" OR "/DocDetails/123abc",
causing the extra '/' at the end, hence throwing an transformer exception.

Related

how to parse xml to java in nodelist

that is my xml
<?xml version = "1.0" encoding = "UTF-8"?>
<ns0:GetADSLProfileResponse xmlns:ns0 = "http://">
<ns0:Result>
<ns0:eCode>0</ns0:eCode>
<ns0:eDesc>Success</ns0:eDesc>
</ns0:Result>
</ns0:GetADSLProfileResponse>
that is my code in java I need to know how to start in this
I tried some code online but still did not solve my problem
how to get the values in the result to loop in it and get 0 in ecode and Success in eDesc
CustomerProfileResult pojo = new CustomerProfileResult();
String body = readfile();
System.out.println(body);
try {
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder db = dbf.newDocumentBuilder();
Document dom = db.parse(new InputSource(new StringReader(body)));
XPath xpath =XPathFactory.newInstance().newXPath();
XPathExpression name = xpath.compile("/xml/GetADSLProfileResponse/Result");
NodeList nodeName = (NodeList) name.evaluate(dom, XPathConstants.NODESET);
if(nodeName!=null){
}
Summary
You can try to following expression which allows you to select nodes without caring the namespace ns0:
/*[local-name()='GetADSLProfileResponse']/*[local-name()='Result']/*
Explanation
In your syntax, several parts were incorrect. Let's take a look together. XPath syntax /xml means that the root node of the document is <xml>, but the root element is <ns0:GetADSLProfileResponse>; GetADSLProfileResponse is incorrect too, because your XML file contains a namespace. Same for Result:
/xml/GetADSLProfileResponse/Result
In my solution, I ignored the namespace, because your namespace provided is incomplet. Here's a full program to get started:
String XML =
"<?xml version = \"1.0\" encoding = \"UTF-8\"?>\n"
+ "<ns0:GetADSLProfileResponse xmlns:ns0 = \"http://\">\n"
+ " <ns0:Result>\n"
+ " <ns0:eCode>0</ns0:eCode>\n"
+ " <ns0:eDesc>Success</ns0:eDesc>\n"
+ " </ns0:Result>\n"
+ "</ns0:GetADSLProfileResponse> ";
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
Document document;
try (InputStream in = new ByteArrayInputStream(XML.getBytes(StandardCharsets.UTF_8))) {
document = builder.parse(in);
}
XPath xPath = XPathFactory.newInstance().newXPath();
XPathExpression expr = xPath.compile("/*[local-name()='GetADSLProfileResponse']/*[local-name()='Result']/*");
NodeList nodeList = (NodeList) expr.evaluate(document, XPathConstants.NODESET);
for (int i = 0; i < nodeList.getLength(); i++) {
Node node = nodeList.item(i);
System.out.println(node.getNodeName() + ": " + node.getTextContent());
}
It prints:
ns0:eCode: 0
ns0:eDesc: Success
See also:
How to query XML using namespaces in Java with XPath?
Node (Java Platform SE 8)

Xpath display correct results in XMLSpy but null in Java

I am trying to display all text within text nodes only, within an XFA XML document while ignoring namespaces.
I came up with an Xpath that returns the desired results within XMLSpy with xpath 1.0 but the same Xpath in Java returns null for some reason.
Xpath = //*[local-name()='text'][string-length(normalize-space(.))>0]
XPathFactory factory = XPathFactory.newInstance();
XPath xpath = factory.newXPath();
ArrayList<String> list = new ArrayList<>();
XPathExpression expr = xpath.compile("//*[local-name()='text'][string-length(normalize-space(.))>0]");
NodeList nodes = (NodeList) expr.evaluate(doc, XPathConstants.NODESET);
for (int i = 0; i < nodes.getLength(); i++) {
System.out.println("This prints null = " + nodes.item(i).getNodeValue());
}
XML file wouldn't post here so it can be viewed at the link below:
https://drive.google.com/file/d/1n-v3gzT-3GgxNnYKFUvMPjRQmtnkqcpY/view?usp=sharing
The problem is that it's not the <text> elements that contain the values, but their child text nodes.
Replace the line
System.out.println("This prints null = " + nodes.item(i).getNodeValue());
with
System.out.println("This does not print null = " + nodes.item(i).getFirstChild().getNodeValue());

Java XPath - select all nodes does not work with namespaces

I would like to select all elements (in my case //ab) from given XML. But it does not select anything (matches variable contains empty list):
XPathFactory xPathfactory = XPathFactory.newInstance();
XPath xpath = xPathfactory.newXPath();
XPathExpression expr = xpath.compile("//ab");
NodeList matches = (NodeList) expr.evaluate(new InputSource(new ByteArrayInputStream(
("<x:xml xmlns:x=\"yyy\">"+
"<x:ab>123</x:ab>"+
"</x:xml>").getBytes())), XPathConstants.NODESET);
if (matches != null)
{
for (int i = 0; i < matches.getLength(); i++)
{
Node node = (Node) matches.item(i);
System.out.println(node.getNodeName());
System.out.println(node.getNodeValue());
}
}
Interesting is when change the xpath to : *//ab , then it does select the ab element.
Also when I remove namespace from xml then the xpath //ab does select the ab element.
Same as when I add namespaceContext to xpath object and add namespace to xpath query: //x:ab , then it does select the ab element.
How should I change the code, so I would get ab element without changing the query?

How to narrow an XPath evaluation to a single node instead of a whole document in Java?

XML stream
<l>
<i>
<a>AAA</a>
<b>BBB</b>
<c>CCC</c>
</i>
<i>
<a>AAA2</a>
<b>BBB2</b>
<c>CCC2</c>
</i>
<i>
...
</i>
</l>
I want to output the following text with some Java code:
> CCC
> CCC2
...
Here is the code I wrote to produce the expected result:
Java code
DocumentBuilder docBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
Document d = docBuilder.parse("file:///C:/path/to/my/xml/stream.xml");
XPath xpath = XPathFactory.newInstance().newXPath();
XPathExpression expr = xpath.compile("//i");
NodeList listOfiNodes = (NodeList) expr.evaluate(d, XPathConstants.NODESET);
for(int i=0;i<listOfiNodes.getLength();i++) {
XPathExpression expr2 = xpath.compile("//c");
System.out.println("> " + ((Node) expr2.evaluate(listOfiNodes.item(i), XPathConstants.NODE)).getTextContent());
}
expr2 keeps on returning the first c node. So I get this output:
> CCC
> CCC
...
The evaluation performed by expr2 doesn't seem to "stay" on the node passed to evaluate() method. Why?
NOTA: I don't want to get the c nodes directly with the xpath //i/c (or /l/i/c).
Java 6
//c selects all matching nodes in the whole document. Use c instead and you will receive this output:
> CCC
> CCC2
Note that you will get an NPE if a Node i does not contain a c in the line where you print the results. The following code should be working as expected:
DocumentBuilder docBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
Document d = docBuilder.parse("stream.xml");
XPath xpath = XPathFactory.newInstance().newXPath();
XPathExpression expr = xpath.compile("//i");
NodeList listOfiNodes = (NodeList) expr.evaluate(d, XPathConstants.NODESET);
for (int i = 0; i < listOfiNodes.getLength(); i++) {
javax.xml.xpath.XPathExpression expr2 = xpath.compile("c");
Node item = listOfiNodes.item(i);
Node node = (Node) expr2.evaluate(item, XPathConstants.NODE);
if (null != node) {
System.out.println("> " + node.getTextContent());
}
}
Change "//c" with ".//c"
XPathExpression expr2 = xpath.compile(".//c");
It will start the search anywhere from the current node instead of the whole document.
XPathExpression expr2 = (XPathExpression) xpath.compile(".//c");
for(int i=0;i<listOfiNodes.getLength();i++) {
System.out.println("> " + ((Node) expr2.evaluate(listOfiNodes.item(i), XPathConstants.NODE)).getTextContent());
}
Output:
CCC
CCC2

Parse XML with XPath & namespaces in Java

Can you help me adjust this code so it manages to parse the XML? If I drop the XML namespace it works:
String webXmlContent = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
"<foo xmlns=\"http://foo.bar/boo\"><bar>baz</bar></foo>";
DocumentBuilderFactory domFactory = DocumentBuilderFactory.newInstance();
domFactory.setNamespaceAware(true);
DocumentBuilder builder = domFactory.newDocumentBuilder();
org.w3c.dom.Document doc = builder.parse(new StringInputStream(webXmlContent));
NamespaceContextImpl namespaceContext = new NamespaceContextImpl();
namespaceContext.startPrefixMapping("foo", "http://www.w3.org/2001/XMLSchema-instance");
XPath xpath = XPathFactory.newInstance().newXPath();
xpath.setNamespaceContext(namespaceContext);
XPathExpression expr = xpath.compile("/foo/bar");
Object result = expr.evaluate(doc, XPathConstants.NODESET);
NodeList nodes = (NodeList) result;
System.out.println("Got " + nodes.getLength() + " nodes");
You must use a prefix in your XPath, e. g.: "/my:foo/my:bar" You can choose any prefix you like - it doesn't have anything to do with the prefixes you use or don't use in the XML file - but you must choose one. This is a limitation of XPath 1.0.
You must perform prefix mapping from "my" to "http://foo.bar/boo" (not to "http://www.w3.org/2001/XMLSchema-instance")

Categories