Tuesday, May 31, 2016

JAXB & XML Binding Using XJC

Recently I was given an XML file with multiple schemas defining the structure of the XML file. Given my comfort level with Java technologies, I decided to use JAXB to marshall/unmarshall XML files. This blog describes the steps I took to work with XML files.

How to auto-generate Java classes that represent XML schemas using XJC

Schemas were placed in multiple sub-directories:

~/myproject/src/main/schema/schema1/*.xsd
~/myproject/src/main/schema/schema2/*.xsd

Running xjc on files in schema2 directory worked fine and Java classes were generated.
However, files in schema1 failed to auto-generate with errors like:

[ERROR] Property "Lang" is already defined. Use <jaxb:property> to resolve this conflict.
line 302 of http://www.w3.org/2002/08/xhtml/xhtml1-strict.xsd

[ERROR] The following location is relevant to the above error
line 303 of http://www.w3.org/2002/08/xhtml/xhtml1-strict.xsd

One of the schemas had a dependency on http://www.w3.org/2002/08/xhtml/xhtml1-strict.xsd. I noticed attribute "lang" was used several times in http://www.w3.org/2002/08/xhtml/xhtml1-strict.xsd. So xjc was not able to generate unique Java classes to represent these repeating attributes. The good news is you can instruct xjc to rename any portion of the schema is having problems with, using a binding customization file.

To fix the above errors, I created a file called: xhtml1-strict.xjb

<bindings xmlns="http://java.sun.com/xml/ns/jaxb"
          xmlns:xsi="http://www.w3.org/2000/10/XMLSchema-instance"
          xmlns:xs="http://www.w3.org/2001/XMLSchema"
          version="2.1">
    <bindings schemaLocation="http://www.w3.org/2002/08/xhtml/xhtml1-strict.xsd" version="1.0">
        <!-- rename the value element -->
        <bindings node="//xs:attributeGroup[@name='i18n']">
            <bindings node=".//xs:attribute[@name='lang']">
                <property name="I18nLangAttribute"/>
            </bindings>
        </bindings>
        <bindings node="//xs:element[@name='bdo']//xs:complexType">
             <bindings node=".//xs:attribute[@name='lang']">
                 <property name="BdoLangAttribute"/>
             </bindings>
        </bindings>
    </bindings>
</bindings>


Run xjc again to auto-generate Java Binding classes:

cd ~noushin/workspace/ajs/src/main/schema
xjc -d ~noushin/workspace/ajs/src/main/java -b xhtml1-strict.xjb */*.xsd

After running the above commands, you should see Java packages and classes in src directory.

The following snippet shows you how to read a XML file with parent tag and parse it to get content of :

public MyDocument getMyDocument() {
    JAXBContext context;
    try {
        context = JAXBContext.newInstance(MyDocument.class);
        Unmarshaller um = context.createUnmarshaller();
        FileReader fileReader = new FileReader("/path/to/xmlfile/);
        MyDocument myDocument = (MyDocument) um.unmarshal(this.getClass().getClassLoader()
     .getResourceAsStream("mydocument.xml"));
        Metadata metadata = null;
        if (myDocument != null) {
               metadata = myDocument.getMetadata();
        }
    }
    catch (FileNotFoundException | JAXBException e) {
        // handle exception
        e.printStackTrace();
    }
}

You will get an exception during unmarshalling if your xml document parent tag is "":
javax.xml.bind.UnmarshalException: unexpected element (uri:"urn:hl7-org:docns", local:"myDocument"). Expected elements are ...

In this case, you need to add the following to your JAXB Class MyDocument:

@XmlRootElement(name = "myDocument")









No comments:

Post a Comment