12

I have an XML file and I know the node name I need to change the value for.

The nodename is ipAddress.

I can use JDOM, get document, get node and change the value and write it or I can write an XSLT file.

The code changing value goes from Java, so my question is which option is better? The size of the XML file can be different.

Another XSLT-related question: Is it possible to write an XSLT file such that I will not be listing all nodes that are in XML but will just specify like if node == ipAddress, then take the new value, and how would I apply the XSLT transformation from Java?

Thank you.

Adi Inbar
  • 12,097
  • 13
  • 56
  • 69
yart
  • 7,515
  • 12
  • 37
  • 37
  • 1
    Good question, +1. See my answer for a complete XSLT solution and a link to the documentation of one of the best Java-based XSLT processor. – Dimitre Novatchev Jan 13 '11 at 00:09
  • What do you mean by "best"? Most convenient (simplest), most performance, usable for big files? XSLT and tree models (DOM) can be convenient, but also use lots of memory and are relatively slow (for example). – StaxMan Jan 13 '11 at 00:31
  • Saxon is pretty much the best. It has a smaller-than-DOM in-memory model, it supports streaming for huge files, has an exceptional optimizer, and supports XSLT 1, XSLT 2, XQuery, XML Schema, and has support for some newer draft standards. – lavinio Jan 13 '11 at 00:43
  • Hello StaxMan. Thank you for your question. I would say what is most convenient and simplest way for handling such cases? – yart Jan 13 '11 at 00:45
  • @Dimitre Novatchev: Thank you for the reminder. I accepted first answer since it was clear how to implement. I like your answer as well but I remember that I spend some time reading about saxonica and still had questions but this is already different topic. Thank you Dimitre. I will go also through all my not yet accepted answers. – yart Jul 08 '11 at 11:11

2 Answers2

6

You could use the standard org.w3c.dom APIs to get a DOM. Then get the node using the standard javax.xml.xpath APIs. And then use the javax.xml.transform APIs to write it back out.

Something like:

import java.io.File;
import javax.xml.parsers.*;
import javax.xml.transform.*;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import javax.xml.xpath.*;
import org.w3c.dom.*;

public class Demo {

    public static void main(String[] args) throws Exception {
        DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
        Document document = dbf.newDocumentBuilder().parse(new File("input.xml"));

        XPathFactory xpf = XPathFactory.newInstance();
        XPath xpath = xpf.newXPath();
        XPathExpression expression = xpath.compile("//A/B[C/E/text()=13]");

        Node b13Node = (Node) expression.evaluate(document, XPathConstants.NODE);
        b13Node.getParentNode().removeChild(b13Node);

        TransformerFactory tf = TransformerFactory.newInstance();
        Transformer t = tf.newTransformer();
        t.transform(new DOMSource(document), new StreamResult(System.out));
    }
}
Adi Inbar
  • 12,097
  • 13
  • 56
  • 69
bdoughan
  • 147,609
  • 23
  • 300
  • 400
4

XSLT solution:

<xsl:stylesheet version="1.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output omit-xml-declaration="yes" indent="yes"/>
  <xsl:strip-space elements="*"/>

  <xsl:param name="pNewIpAddress" select="'192.68.0.1'"/>

  <xsl:template match="node()|@*">
    <xsl:copy>
       <xsl:apply-templates select="node()|@*"/>
    </xsl:copy>
  </xsl:template>

  <xsl:template match="ipAddress/text()">
  <xsl:value-of select="$pNewIpAddress"/>
  </xsl:template>
</xsl:stylesheet>

When this transformation is applied on any document, all nodes of the document are copied "as-is" except for the text-node child of any ipAddress element (regardless where this element is in the document). The latter is replaced with the value of an externally provided parameter, named $pNewIpAddress.

For example, if the transformation is applied against this XML document:

<t>
    <a>
        <b>
          <ipAddress>127.0.0.1</ipAddress>
        </b>
        <c/>
    </a>
    <d/>
</t>

the wanted, correct result is produced:

<t>
   <a>
      <b>
         <ipAddress>192.68.0.1</ipAddress>
      </b>
      <c/>
   </a>
   <d/>
</t>

There are many Java-based XSLT processors and the proper place to understand how they can be invoked from Java is their documentation. One of the best such XSLT processors is Saxon and its documentation can be found at:

http://www.saxonica.com/documentation/documentation.xml

Adi Inbar
  • 12,097
  • 13
  • 56
  • 69
Dimitre Novatchev
  • 240,661
  • 26
  • 293
  • 431
  • Thank you very much for your answer. Do you know if I can pass the value of new ip address through the variable from java code like transformer.transform(xml, xslt, variable) so I will not specify it in xslt file directly since the value is dynamically calculated? – yart Jan 13 '11 at 01:58
  • @yart: Yes. See the Saxon documentation here: http://www.saxonica.com/documentation/using-xsl/embedding.xml – Dimitre Novatchev Jan 13 '11 at 02:35