简体   繁体   中英

Filtering XML Document using XPATH in java

I have following XML Document which i need to filter using xpath expression :

<List>
<Item>
<Name>abc</Name>
<Category><Name>123</Name><value>aaa</value></Category>
</Item>
<Item>
<Name>def</Name>
<Category><Name>456</Name><value>bbb</value></Category>
</Item>
<Item>
<Name>xyz</Name>
<Category><Name>123</Name><value>ccc</value></Category>
</Item>
</List>

I am providing the Following Expression : "/List/Item/Category[Name='123']", which gives me :

<Category>
<Name>123</Name>
<value>aaa</value>
<Category>
<Name>123</Name>
<value>ccc</value>
</Category>
</Category>

But I need this Output :

<List>
    <Item>
    <Name>abc</Name>
    <Category><Name>123</Name><value>aaa</value></Category>
    </Item>
    <Item>
    <Name>xyz</Name>
    <Category><Name>123</Name><value>ccc</value></Category>
    </Item>
    </List>

xPath will return you the node's that match your criteria from within the current Document

If you want to generate a "filtered" representation of the Document , then you need to create a second Document and append the matching Node s to it

For example...

try {
    DocumentBuilderFactory f = DocumentBuilderFactory.newInstance();
    DocumentBuilder b = f.newDocumentBuilder();
    Document original = b.parse(...);
    original.getDocumentElement().normalize();

    Document filtered = b.newDocument();
    Node root = filtered.createElement("List");
    filtered.appendChild(root);

    String expression = "/List/Item/Category[Name='123']";
    XPath xPath = XPathFactory.newInstance().newXPath();
    Object result = xPath.compile(expression).evaluate(original, XPathConstants.NODESET);

    NodeList nodes = (NodeList) result;
    for (int i = 0; i < nodes.getLength(); i++) {

        Node node = nodes.item(i);
        filtered.adoptNode(node);
        root.appendChild(node);

    }

    try (ByteArrayOutputStream os = new ByteArrayOutputStream()) {

        Transformer tf = TransformerFactory.newInstance().newTransformer();
        tf.setOutputProperty(OutputKeys.INDENT, "yes");
        tf.setOutputProperty(OutputKeys.METHOD, "xml");
        tf.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "4");

        DOMSource domSource = new DOMSource(filtered);
        StreamResult sr = new StreamResult(os);
        tf.transform(domSource, sr);

        String text = new String(os.toByteArray());
        System.out.println(text);

    } catch (TransformerException ex) {
        ex.printStackTrace();
    }

} catch (ParserConfigurationException | SAXException | IOException | XPathExpressionException | DOMException exp) {
    exp.printStackTrace();
}

So, based on your original XML, this will output

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<List>
    <Category>
      <Name>123</Name>
      <value>aaa</value>
    </Category>
    <Category>
      <Name>123</Name>
      <value>ccc</value>
    </Category>
</List>

Just beware, that this will be removing the nodes from the original document when they are appended to the new one.

i want to just filter the result without losing the xml structure

XPath is a lot like SQL, it's a query language, it's job isn't to generate a new structure. Having said that, you can control which node it returns, for example, if you change the query to something more like this...

String expression = "/List/Item[Category[Name='123']]";

Then the output becomes something more like...

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<List>
    <Item>
    <Name>abc</Name>
    <Category>
      <Name>123</Name>
      <value>aaa</value>
    </Category>
  </Item>
    <Item>
    <Name>xyz</Name>
    <Category>
      <Name>123</Name>
      <value>ccc</value>
    </Category>
  </Item>
</List>

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