Creating an XML document based on my POJO domain model – how will JAXB help me?

The challenge is a common one. We have got data in a domain model, based on POJOs. And we need to marshall that data into an XML document. Note that the exact structure of that document is not so important – we will probably translate is somewhere anyway. At this point, all we have is a set of POJO definitions. There is no XSD, no predefined XML structure.

JAXB – the Java Architecture for XML Binding – seems like the technology to turn to. My experience with JAXB has mainly been from the other end: we have XML data, described by an XSD, that we want to turn into Java Objects (unmarshall XML to Java). In that case, we take the XSD that describes the XML, generate Java classes and use JAXB to unmarshall XML into objects based on those classes. The alternative round – taking classes and marshalling them into XML would be just as simple – or even simpler, now would it not?

Well, it was not as straightforward as I hoped it would be. I thought that adding a few JAXB annotations to my existing POJO model would do the trick – but no such luck. Unless I am completely misguided and mistaken, the steps required are:

  • create an XSD based on the original POJO model
  • generate JAXB annotated classes from that XSD schema (classes that very similar to but not exactly the same as the original POJOs)
  • use the JAXB ObjectFactory to create instances of the JAXB-POJOs (rather than simply new Object() or new Object(a,b,c) on the original POJOs)
  • use the JAXB Context and the generated ObjectFactory class to marshall the JAXB-POJO object graph into an XML document

The original POJO model is a very simple one: the Company class has a name property and an employees property – which is a List<Employee>.

Image

Here is a snippet of the XML document that we will produce:

Image

This article uses JDeveloper 11gR2 for demonstrating the steps with JAXB – however, the steps will be similar and the code exactly the same in other IDEs.

I started out by adding one or two JAXB annotations to my existing POJOs –

Image

and had the idle and naive hope that that was all it would take to marshall an XML document.

Image

More action was definitely required.

Generate the XSD based on the POJO definitions

Some Googling and browsing taught me that the JDK ships with a tool called schemagen. It can be used to generated an XML Schema Definition from class definitions. It can be found in the bin directory of the JDK installation:

Image

And it can be ran from the command line to generate XSD files based on Java source files of class definitions. Schemagen takes parameters: -d is used to specify the target directory for generating the XSD files. -cp is used to specify the classpath (root directory) for the Java classes for which the schema is to be generated. In this case, classes Emp and Company in package nl.amis.hrm are to be used in generating the schema. These classes are found in the classes directory in the project – corresponding to the absolute path reference C:\Temp\ConsumeXMLusingJAXB\Hrm\classes.

Image

The schema generation results in fairly horribly named XSD files, found in the specified destination directory:

Image

These XSD files describe in XML terms the structure that the POJOs represent.

Generate JAXB Annotated Classes based on XSDs

The next step in the journey to XML marshalling is the generation of the JAXB annotated classes based on the XSD schemas. Note: we can edit the XSDs before actually generating these classes, in order to achieve “better” XML. We will not do that right now.

From the context menu on the top schema (schema1.xsd), use the option Generate | JAXB 2.0 Content Model to generate the JAXB annotated classes – counterparts to the original POJOs – and the ObjectFactory.

Image

Set the wizard options as desired:

Image

The resulting project structure and components:

Image

Use the JAXB ObjectFactory to create instances of the JAXB-POJOs (rather than simply new Object() or new Object(a,b,c) on the original POJOs)

Instantiating our domain objects is not as simple with the generated jaxb classes as it was with the original domain classes. Sure, they are similar. But in order to create instances that are marshallable by JAXB, we have to use the ObjectFactory class to create instances, rather than just the constructor.

I have to change some of the original code that created a List of Emp objects in the Company, to work with the ObjectFactory:

...
    public Company () {
        resetData();

    }
    public List<Emp> getEmployees() {
        if (employees == null) {
            employees = new ArrayList<Emp>();
        }
        return this.employees;
    }

    public void resetData() {
        createEmp((long)7369, "SMITH", "CLERK", (long)7902,
                  (double)800,
                  null, (long)20);
        createEmp((long)7499, "ALLEN", "SALESMAN", (long)7698,
                  (double)1600,
                  (double)300, (long)30);
      ...
    }

    public void createEmp(Long empno, String ename, String job, Long mgr,
                           Double sal, Double comm,
                          Long deptno) {
        Emp newEmp =         new ObjectFactory().createEmp();

        newEmp.update(empno, ename, job, mgr, sal, comm, deptno);
        getEmployees().add(newEmp);
    }

Marshall the JAXB-POJO object graph into an XML document

With the JAXB annotated classes and the ObjectFactory ready to rock, the creation of my first XML document is only a small effort. I have created a class XMLProducer. This class acquires an JAXB context for the package that contains the ObjectFactory. It uses the ObjectFactory to create an instance of Company (that includes instances of Emp). A Marshaller is retrieved from the JAXB Context and used to marshall the object graph to an XML document, written out in this case to the system output.

public class XMLProducer {

    public static void main(String[] args) throws JAXBException {
        JAXBContext jaxbContext = JAXBContext.newInstance("nl.amis.hrm.jaxb");
        Marshaller marshaller = jaxbContext.createMarshaller();
        marshaller.setProperty("jaxb.formatted.output", Boolean.TRUE);
        ObjectFactory objf = new ObjectFactory();
        Company company = objf.createCompany();
        company.setName("AMIS and Friends");
        marshaller.marshal(company, System.out);
    }
}

To have an existing POJO model work together smoothly with the JAXB POJOs, we probably need to work with mapper classes that convert from JAXB POJO to domain POJO and vv. Pity, that additional step.

Resources

Download JDeveloper 11gR2 project with this article’s resources: ProduceXMLusingJAXB.

Tags:, , ,