XML Schema with different namespaces & JAXB classes generation

Goal

To create XML schemas with types in different namespaces for JAXB class generation and marshalling/unmarshalling

Description

Recently, I worked in a project where I had to invoke a WS where the result, at the end path, was given by the following

@XmlAnyElement(lax = true)
protected List<Object> any;

where, each element was an instance of org.w3c.dom.Element (actually, it was com.sun.org.apache.xerces.internal.dom.ElementNSImpl, but that is not really important for this recipe). So, and just to make things clear, the SOAP response was something like the following (the response was a little changed due to confidentiality reasons):

...
  <ROOT_NODE>
    <TaskId>cbf5c393-6daa-11e5-efcb-a7761bfc9dfb</TaskId>
    <ParentTaskId>cbf5c393-6daa-11e5-efcb-a7761bfc9dfb</ParentTaskId>
    <SourceInstanceId>93724655-773f-11e5-efcb-a76be3c69df5</SourceInstanceId>
    ...
    <TaskData>
      <ns1:CommonModel type="put">
        <ns2:CommonData>
          <ns2:Valor>2050.00</ns2:Valor>
          <ns2:Descricao>Porque tem que ser</ns2:Descricao>
          <ns2:Tipo/>
          ...
        </ns2:CommonData>
      </ns1:CommonModel>
    </TaskData>
  </ROOT_NODE>
..

I had to return the response in a structured way, processing each of the elements and storing them in appropriate fields such as taskId, of parentTaskId. Each of my elements from the previously referred “List<Object> any;” was mapped to one of the children of the ROOT_NODE. Therefore, it was more or less easy to map each of the simple elements such as TaskId, with code such as:

final Map<String, Object> values = new HashMap<String, Object>();
for (final Object object : response.getAny()) {
  final Element element = (Element)object;
  values.put(element.getLocalName(), element.getTextContent());
}

However, for more complex structures such as the TaskData element, the code was not that simple. So, I had the need to create an XML schema from the XML response, generate the corresponding JAXB classes and add an if clause to deal differently when the element.getLocalName() is TaskData, and process that element using JAXB unmarshalling. This recipe will explain the steps I took.

How to

The first thing I did was to extract the XML part I wanted to map with JAXB classes and create an XML schema for it. One of the trickiest parts for me here was the need to create XML schemas with different namespaces. I solved that with two different XML schemas, the main one with the default namespace and a second one, imported into the first, with the second namespace. The first file was named CommonModel.xsd and it was the one that defined the schema for the CommonModel element:

<?xml version="1.0" encoding="utf-16"?>
<xsd:schema attributeFormDefault="unqualified"
  elementFormDefault="qualified" version="1.0"
  xmlns:xsd="http://www.w3.org/2001/XMLSchema" targetNamespace="http://<namespace1>"
  xmlns:ns2="http://<namespace2>">
  <xsd:import schemaLocation="CommonData.xsd" namespace="http://schemas.cordys.com/" />
  <xsd:element name="CommonModel">
    <xsd:complexType>
      <xsd:sequence>
        <xsd:element ref="ns2:CommonData" />
      </xsd:sequence>
      <xsd:attribute name="type" type="xsd:string" />
    </xsd:complexType>
  </xsd:element>
</xsd:schema>

The second XML schema file was named CommonData.xsd and it was the one that defined the schema for the CommonData element with the second namespace:

<?xml version="1.0" encoding="utf-16"?>
<xsd:schema attributeFormDefault="unqualified"
  elementFormDefault="qualified" version="1.0"
  xmlns:xsd="http://www.w3.org/2001/XMLSchema" targetNamespace="http://<namespace2">
  <xsd:element name="CommonBusinessData">
    <xsd:complexType>
      <xsd:sequence>
        <xsd:element name="Valor" type="xsd:decimal" />
        <xsd:element name="Descricao" type="xsd:string" />
        <xsd:element name="Tipo" type="xsd:string" />
         ...
      </xsd:sequence>
    </xsd:complexType>
  </xsd:element>
</xsd:schema>

Next, and since I was using maven, I defined the plugin jaxb2-maven-plugin so that the JAXB code generation takes place with the files previously defined (by the way, both were defined inside src/main/xsd):

...
  <plugins>
    ...
    <plugin>
      <groupId>org.codehaus.mojo</groupId>
      <artifactId>jaxb2-maven-plugin</artifactId>
      <configuration>
        <extension>true</extension>
        <packageName>com.wordpress.zenidas.myproject</packageName>
      </configuration>
      <executions>
        <execution>
          <goals>
            <goal>xjc</goal>
          </goals>
        </execution>
      </executions>
    </plugin>
    ...
  </plugins>
...

With the previous definitions, when I ran the command mvn package, the following was generated into target/generated-sources/jaxb, in the defined package com.wordpress.zenidas.myproject:

  • package-info.java (notice the definition of the default namespace that will be used by the classes in the package, noted in bold):
    @javax.xml.bind.annotation.XmlSchema(namespace = "http://<namespace1>", elementFormDefault = javax.xml.bind.annotation.XmlNsForm.QUALIFIED)
    package com.wordpress.zenidas.myproject;
    
  • CommonData.java (notice the overridden definition of the namespace in the @XmlRootElement annotation):
    @XmlAccessorType(XmlAccessType.FIELD)
    @XmlType(name = "", propOrder = {
        "valor",
        "descricao",
        "tipo",
        "businessAction"
    })
    @XmlRootElement(name = "CommonData", namespace = "http://<namespace2>")
    public class CommonData {
    
      @XmlElement(name = "Valor", namespace = "http://<namespace2>", required = true)
      protected BigDecimal valor;
      ...
    }
    
  • CommonModel.java (notice the definition of the classe’s attribute commonData with the appropriate @XmlElement annotation and the corresponding namespace):
    @XmlAccessorType(XmlAccessType.FIELD)
    @XmlType(name = "", propOrder = { "commonData" })
    @XmlRootElement(name = "CommonModel")
    public class CommonModel {
    
      @XmlElement(name = "CommonData", namespace = "http://<namespace2>", required = true)
      protected CommonData commonData;
      @XmlAttribute(name = "type")
      protected String type;
      ...
    }
    
  • ObjectFactory.java (code is not relevant)

Finally, to test my implementation, I have created an XML file with the contents I had in the beginning and created a simple Java class to test the unmarshalling operation (the code should print two lines, both not null):

public class MyTestClass {

  public static void main(String[] args) throws JAXBException {
    JAXBContext jc = JAXBContext.newInstance(ObjectFactory.class);
    final Unmarshaller unmarshaller = jc.createUnmarshaller();
    
    CommonModel instance = (CommonModel) unmarshaller.unmarshal(new File("myfile.xml"));

    System.out.println(instance);
    // it used to print null, when the namespace was not properly set
    System.out.println(instance.getCommonData());
  }
}

Explanations

And that’s it. Since I had to struggle a little bit with the definition of different namespaces for my code generation and unmarshalling to succeed, I have created this recipe which, hopefully, will be of any use to more people, including myself when, sometime in the future, maybe, I will have the exact same need. No more explanations seem to be necessary at this point, as all of them were made in place, as code was being shown in the previous sections.

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