简体   繁体   中英

XML XPath Parsing in Java

This is the following standard code to parse XML using XPath in Java. I am unable to debug why I am getting null values. I have attached the java file, the xml file and the output. Would appreciate if somebody can explain a little where I am going wrong. Thanks in advance! :)

XPathParser.java

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpression;
import javax.xml.xpath.XPathFactory;

import org.w3c.dom.Document;
import org.w3c.dom.NodeList;

public class XPathParser {
    public static void main(String args[]) throws Exception {
        //loading the XML document from a file
        DocumentBuilderFactory builderfactory = DocumentBuilderFactory.newInstance();
        builderfactory.setNamespaceAware(true);

        //XML read
        DocumentBuilder builder = builderfactory.newDocumentBuilder();
        Document xmlDocument = builder.parse("Stocks.xml");

        // Creates a XPath factory
        XPathFactory factory = javax.xml.xpath.XPathFactory.newInstance();

        //Creates a XPath Object
        XPath xPath = factory.newXPath();

        //Compiles the XPath expression
        //XPathExpression xPathExpression_count = xPath.compile("count(//stock)");
        XPathExpression xPathExpression = xPath.compile("//stock");

        //Run the query and get a nodeset
        Object result = xPathExpression.evaluate(xmlDocument,XPathConstants.NODESET);

        //Cast the result into a DOM nodelist
        NodeList nodes = (NodeList) result;
        System.out.println(nodes.getLength());
        System.out.println(nodes.item(0));
        for (int i=0; i<nodes.getLength();i++){
          System.out.println(nodes.item(i).getNodeValue());
        }
    }
}

Stocks.xml

<?xml version="1.0" encoding="UTF-8"?>
<stocks>
       <stock>
              <symbol>ABC</symbol>
              <price>10</price>
              <quantity>50</quantity>
       </stock>
       <stock>
              <symbol>XYZ</symbol>
              <price>20</price>
              <quantity>1000</quantity>
       </stock>
</stocks>

OUTPUT:

2
[stock: null]
null
null

You are trying calling the getNodeValue method on Stock nodes - this doesn't make sense as they do not have a value, they are parent nodes.

You can loop over the child nodes of Stock and look for information:

final Document document = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(new ByteArrayInputStream(xml.getBytes()));
final XPathExpression expression = XPathFactory.newInstance().newXPath().compile("//stock");
final NodeList nodeList = (NodeList) expression.evaluate(document, XPathConstants.NODESET);
for (int i = 0; i < nodeList.getLength(); ++i) {
    final NodeList childList = ((Element) nodeList.item(i)).getChildNodes();
    for (int j = 0; j < childList.getLength(); ++j) {
        final Node node = childList.item(j);
        if (node.getNodeType() == Node.ELEMENT_NODE) {
            System.out.println(node.getNodeName() + "=" + node.getTextContent());
        }
    }
}

Output:

symbol=ABC
price=10
quantity=50
symbol=XYZ
price=20
quantity=1000

Note that you have to filter the child Node s by type otherwise you loop over a combination of child nodes and the node's text value, which comes up as a text node. This is a common gotcha in walking through XML in this manner.

You can also loop over all child text nodes of Stock :

final Document document = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(new ByteArrayInputStream(xml.getBytes()));
final XPathExpression expression = XPathFactory.newInstance().newXPath().compile("//stock/*/text()");
final NodeList nodeList = (NodeList) expression.evaluate(document, XPathConstants.NODESET);
for (int i = 0; i < nodeList.getLength(); ++i) {
    final Node node = nodeList.item(i);
    System.out.println(node.getNodeValue());
}

Output:

ABC
10
50
XYZ
20
1000

In this situation you loop over all text nodes that the children of the children of Stock - this means you lose information on the node name. But you can recreate the first approach by looping over all children of Stock that are not text nodes:

final Document document = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(new ByteArrayInputStream(xml.getBytes()));
final XPathExpression expression = XPathFactory.newInstance().newXPath().compile("//stock/*");
final NodeList nodeList = (NodeList) expression.evaluate(document, XPathConstants.NODESET);
for (int i = 0; i < nodeList.getLength(); ++i) {
    final Node node = nodeList.item(i);
    System.out.println(node.getNodeName() + "=" + node.getTextContent());
}

Output:

symbol=ABC
price=10
quantity=50
symbol=XYZ
price=20
quantity=1000

Or you can select a specific child node in the XPath if you want something more specific:

final Document document = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(new ByteArrayInputStream(xml.getBytes()));
final XPathExpression expression = XPathFactory.newInstance().newXPath().compile("//stock/symbol/text()");
final NodeList nodeList = (NodeList) expression.evaluate(document, XPathConstants.NODESET);
for (int i = 0; i < nodeList.getLength(); ++i) {
    final Node node = nodeList.item(i);
    System.out.println(node.getNodeValue());
}

Output:

ABC
XYZ

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM