8
\$\begingroup\$

I have a need to extract a part of XML tree (everything under root) and convert it to a string. (The result string will be later pasted inside XSL-FO XSLT file.)

The above functionality is a part of Java 6 application. I use org.w3c.dom and javax.xml.transform. Still in the debugging process.

// ...

// Extract all child notes of root from foDoc
// and convert the result to String
Node root = foDoc.getElementsByTagNameNS("*","root").item(0);
NodeList body = root.getChildNodes();
String foResult;

try {
    foResult = nodeListToString(body);
} catch (TransformerException e) {
    logger.error(e);

    foResult = "";
}
// ...


private static String nodeListToString(NodeList nodes) throws TransformerException {
    StringBuilder result = new StringBuilder();
    int len = nodes.getLength();
    for(int i = 0; i < len; ++ i) {
        result.append(nodeToString(nodes.item(i)));
    }
    return result.toString();
}

private static String nodeToString(Node node) throws TransformerException {
    StringWriter buf = new StringWriter();
    Transformer xform = TransformerFactory.newInstance().newTransformer();
    xform.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
    xform.transform(new DOMSource(node), new StreamResult(buf));
    return(buf.toString());
}

My questions:

  1. Assuming that there will always be exactly one root node, how reliable is my code in extracting everything under it?

  2. Am I missing any edge cases?

  3. Anything just plain wrong?

Please disregard any exception handling inadequacies.

Edited on 10/06/2015

Since my originally posted code had a "just plain wrong" part, I've fixed it. There were no answers based on the old code.

\$\endgroup\$
2
  • 2
    \$\begingroup\$ How committed are you to using DOM? I seldom just jump ion and recommend alternative tools, but, I maintain the JDOM library, and this sort of activity is easy if you can use JDOM instead..... just saying. \$\endgroup\$
    – rolfl
    Oct 3, 2015 at 21:56
  • \$\begingroup\$ @rolfl - Thank you for the reference. I will keep it in mind for future projects. This time I am making a small change to a plugin for a huge system and need to stay within its existing tooling. \$\endgroup\$
    – PM 77-1
    Oct 6, 2015 at 19:10

1 Answer 1

5
+25
\$\begingroup\$

Reading the documentation for Transformer, where it says:

A Transformer may be used multiple times. Parameters and output properties are preserved across transformations.

, I'd say that not only can the Transformer object be reused across multiple nodes, but you should also be able to use a single StringWriter instead of recreating it for every node; same goes for the DOMSource actually:

private static String nodeListToString(NodeList nodes) throws TransformerException {
    DOMSource source = new DOMSource();
    StringWriter writer = new StringWriter();
    StreamResult result = new StreamResult(writer);
    Transformer transformer = TransformerFactory.newInstance().newTransformer();
    transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");

    for (int i = 0; i < nodes.getLength(); ++i) {
        source.setNode(nodes.item(i));
        transformer.transform(source, result);
    }

    return writer.toString();
}

That said, I'm only guessing that this should work.

Also, the len variable is probably going to be inlined automatically anyway, though you could check that with a disassembler just to be sure (it wouldn't make that much of a difference I suspect).

Otherwise looks good to me; if you've tested it with all kinds of node types and the output looks good, then I'd say that should work IMHO.

\$\endgroup\$
3
  • \$\begingroup\$ I use nodeToString() in other places of my code as well. \$\endgroup\$
    – PM 77-1
    Oct 7, 2015 at 21:04
  • \$\begingroup\$ Then keep it in addition to this nodeListToString? I'm just assuming that inlining and removing allocations helps with bigger documents. \$\endgroup\$
    – ferada
    Oct 7, 2015 at 21:26
  • 1
    \$\begingroup\$ I second that it looks strange to build a string in a StringWriter just so that it can be appended to a StringBuilder in the caller. It would be better to reuse the StringWriter. You could pass the StreamResult object as a 2nd parameter into nodeListToString \$\endgroup\$
    – janos
    Oct 10, 2015 at 20:21

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.