Search code examples
javadomdom-traversal

transform dom node recursively using Java


Say I have a dom node like so:

org.w3c.dom.Element node:

<node id="101">
  <node id="102">
    <node id="103" />
  </node>
  <node id="104">
    <node id="103" />
  </node>
</node>

To transform the id attribute I would do the following in my Java Code:

String nodeId = node.getAttribute("id");
String newNodeId = "prefix/" + nodeId;
node.getAttributeNode("id").setValue(newNodeId);

The above node would then is then transformed to:

<node id="prefix/101">
  <node id="102">
    <node id="103" />
  </node>
  <node id="104">
    <node id="103" />
  </node>
</node>

But, I want to recursively modify all the subnodes. The expected transformed node is:

<node id="prefix/101">
  <node id="prefix/102">
    <node id="prefix'103" />
  </node>
  <node id="prefix/104">
    <node id="prefix/103" />
  </node>
</node>

I could loop through the child nodes but then node may have multiple levels of children. In this case, the root has two sub-levels. But, if there are more sub-levels then looping through child nodes of every level seems a bit awkward. Is there a more straight forward approach to achieve this task?

Thanks, Sony


Solution

  • You could use XPath to get a list of all the nodes you want to modify:

    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.Element;
    import org.w3c.dom.NodeList;
    import org.xml.sax.InputSource;
    
    ...
    
    final String xml = "<node id=\"101\"><node id=\"102\"><node id=\"103\" /></node><node id=\"104\"><node id=\"103\" /></node></node>";
    final DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
    final DocumentBuilder db = dbf.newDocumentBuilder();
    final Document doc = db.parse(new InputSource(new StringReader(xml)));
    final XPathFactory xpathFactory = XPathFactory.newInstance();
    final XPath xpath = xpathFactory.newXPath();
    final NodeList nodes = (NodeList) xpath.compile("//node[@id]").evaluate(doc, XPathConstants.NODESET);
    for (int nodeNumber = 0; nodeNumber < nodes.getLength(); ++nodeNumber) {
        final Element node = (Element) nodes.item(nodeNumber);
        final String nodeId = node.getAttribute("id");
        final String newNodeId = "prefix/" + nodeId;
        node.getAttributeNode("id").setValue(newNodeId);
    }
    

    The document will now consist of this:

    <?xml version="1.0" encoding="UTF-8" standalone="no"?>
    <node id="prefix/101">
        <node id="prefix/102">
            <node id="prefix/103" />
        </node>
        <node id="prefix/104">
            <node id="prefix/103" />
        </node>
    </node>