Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to insert/replace XML tag in XmlDocument?

Tags:

java

xml

xquery

I have a XmlDocument in java, created with the Weblogic XmlDocument parser.

I want to replace the content of a tag in this XMLDocument with my own data, or insert the tag if it isn't there.

<customdata>
   <tag1 />
   <tag2>mfkdslmlfkm</tag2>
   <location />
   <tag3 />
</customdata>

For example I want to insert a URL in the location tag:

<location>http://something</location>

but otherwise leave the XML as is.

Currently I use a XMLCursor:

    XmlObject xmlobj = XmlObject.Factory.parse(a.getCustomData(), options);
    XmlCursor xmlcur = xmlobj.newCursor();

    while (xmlcur.hasNextToken()) {
      boolean found = false;
      if (xmlcur.isStart() && "schema-location".equals(xmlcur.getName().toString())) {
        xmlcur.setTextValue("http://replaced");
        System.out.println("replaced");
        found = true;
      } else if (xmlcur.isStart() && "customdata".equals(xmlcur.getName().toString())) {
        xmlcur.push();
      } else if (xmlcur.isEnddoc()) {
        if (!found) {
          xmlcur.pop();
          xmlcur.toEndToken();
          xmlcur.insertElementWithText("schema-location", "http://inserted");
          System.out.println("inserted");
        }

      }
      xmlcur.toNextToken();
    }

I tried to find a "quick" xquery way to do this since the XmlDocument has an execQuery method, but didn't find it very easy.

Do anyone have a better way than this? It seems a bit elaborate.

like image 429
svrist Avatar asked Aug 19 '08 08:08

svrist


2 Answers

How about an XPath based approach? I like this approach as the logic is super-easy to understand. The code is pretty much self-documenting.

If your xml document is available to you as an org.w3c.dom.Document object (as most parsers return), then you could do something like the following:

// get the list of customdata nodes
NodeList customDataNodeSet = findNodes(document, "//customdata" );

for (int i=0 ; i < customDataNodeSet.getLength() ; i++) {
  Node customDataNode = customDataNodeSet.item( i );

  // get the location nodes (if any) within this one customdata node
  NodeList locationNodeSet = findNodes(customDataNode, "location" );

  if (locationNodeSet.getLength() > 0) {
    // replace
    locationNodeSet.item( 0 ).setTextContent( "http://stackoverflow.com/" );
  }
  else {
    // insert
    Element newLocationNode = document.createElement( "location" );
    newLocationNode.setTextContent("http://stackoverflow.com/" );
    customDataNode.appendChild( newLocationNode );
  }
}

And here's the helper method findNodes that does the XPath search.

private NodeList findNodes( Object obj, String xPathString )
  throws XPathExpressionException {

  XPath xPath = XPathFactory.newInstance().newXPath();
  XPathExpression expression = xPath.compile( xPathString );
  return (NodeList) expression.evaluate( obj, XPathConstants.NODESET );
}
like image 132
Cheekysoft Avatar answered Sep 17 '22 17:09

Cheekysoft


How about an object oriented approach? You could deserialise the XML to an object, set the location value on the object, then serialise back to XML.

XStream makes this really easy.

For example, you would define the main object, which in your case is CustomData (I'm using public fields to keep the example simple):

public class CustomData {
  public String tag1;
  public String tag2;
  public String location;
  public String tag3;
}

Then you initialize XStream:

XStream xstream = new XStream();
// if you need to output the main tag in lowercase, use the following line
xstream.alias("customdata", CustomData.class);  

Now you can construct an object from XML, set the location field on the object and regenerate the XML:

CustomData d = (CustomData)xstream.fromXML(xml);
d.location = "http://stackoverflow.com";
xml = xstream.toXML(d);

How does that sound?

like image 40
Olly Avatar answered Sep 20 '22 17:09

Olly