Tip: Removing namespaces from XML elements using XMLBeans

I’m a big fan of Apache XMLBeans as it makes parsing, analysing and creating XML documents valid to XML Schemas incredibly easy. A really nice feature of XMLBeans is auto-typing. You can ask it to parse an arbitrary XML document and it will detect if the structure of the XML validates against a XML Schema that has been made known to it. If it does, then an appropriate subclass of the XMLBeans base class (XmlObject, similar in concept to java.lang.Object) is returned by the parser. The subclass allows you to traverse the document as if it is simply a collection of Java beans. Nice!

However you can run into problems if you need to handle multiple XML documents in the default namespaces and with the same root element name. With XMLBeans you generate Java source code from XML Schemas (command line, via Ant task etc.) to produce the aforementioned subclass, and during this process the schema’s target namespace is used to determine the Java classes package structure. So you get Java class naming conflicts. You can override the package name when generating XMLBeans, however there is still issues with auto-typing. My preferred solution to this is to give each of the XML schemas a target namespace, and then tell the XMLBeans parser to substitute the default namespace for whichever one I set in the XML schema when parsing a corresponding XML document. Then the auto-typing works very well.

However, if you require to serialise the XMLBean at a later time, chances are that you require the XML elements to be in the default namespace like they were originally. There are numerous questions posted about how to do this and the majority of answers focus on using XmlOptions to influence the XMLBeans serialiser. For some reason, none of the proposed solutions work for me, so I thought it may be useful to others in a similar situation if I share my solution. XmlCursor to the rescue! Below is a complete source code listing covering the entire solution, tested with XMLBeans 2.0.

schema.xsd


<?xml version="1.0"?>
<xs:schema 
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    elementFormDefault="qualified"
    attributeFormDefault="unqualified"
    targetNamespace="http://my.domain/something">

  <xs:element name="foo">
    <xs:complexType>
      <xs:sequence>
        <xs:element name="bar" type="xs:string"/>
      </xs:sequence>
    </xs:complexType>
  </xs:element>

</xs:schema>

XMLProcessor.java


import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import org.apache.xmlbeans.XmlObject;
import org.apache.xmlbeans.XmlOptions;
import org.apache.xmlbean.XmlException;
import org.apache.xmlbeans.XmlCursor;
import javax.xml.namespace.QName;
import java.util.Map;
import java.util.HashMap;

public class XMLProcessor {
public void process(InputStream input, OutputStream output)
    throws IOException, XmlException {

  // Deserialise the XML document without namespaced elements

  XmlOptions xmlOptions = new XmlOptions();
  Map nsSubstitutionMap = new java.util.HashMap();
  nsSubstitutionMap.put("", "http://my.domain/something");
  xmlOptions.setLoadSubstituteNamespaces(nsSubstitutionMap);

  // Demonstrate auto-typing

  XmlObject doc = XmlObject.Factory.parse(input);
  if (doc instanceof domain.my.something.FooDocument) {
    
    // Do some arbitrary process with the XML document (adding new nodes, change text node values etc.)

    domain.my.something.FooDocument fooDoc = (domain.my.something.FooDocument)doc;
    domain.my.something.FooDocument.Foo rootEl = fooDoc.getFoo();
    rootEl.setBar("new value");
  }

  // Do some arbitrary processing with the XML document with namespaced elements

  domain.my.something.FooDocument.Foo rootEl = doc.getFoo();
  rootEl.setBar("new value");

  // Finally produce the serialised XML document without namespaced elements

  XmlCursor cursor = doc.newCursor();
  cursor.selectPath("//*");
  while (cursor.toNextSelection()) {
    cursor.setName(new QName(cursor.getName().getLocalPart()));
  }

  doc.save(output);
}
}
Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s