Question

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

Was it helpful?

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>

OTHER TIPS

If the prefix value you are appending to the id attribute will be constant you could use XSLT. (This wouldn't be the exact code you'd need... just an example off the top of my head)

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:template match="node">
    <xsl:element name="node">
      <xsl:attribute name="id">
       <xsl:value-of select="concat('prefix/', @id)" />
      </xsl:attribute>

      <xsl:apply-templates/>
    <xsl:element>
  </xsl:template>
</xsl:stylesheet>

This would result in simpler code, but you would have to load an XSLT interpreter such as xalan to perform the transformation. If you aren't using XSLT already this might or might not be the best approach, but is another alternative to directly manipulating the DOM structure with Java code.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top