January 25, 2011

JAXB and Choosing the List Implementation

For elements with max occurs greater than one, JAXB will generate a java.util.List property and the underlying implementation will be java.util.ArrayList.  You can control which list implementation is used through internal and external schema annotations.  You can also use your own domain objects which gives you full control of your object model.  This post will discuss these different options.

Option #1 - Default JAXB Generation

We will use the following XML schema for this post:

<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema
    xmlns:xsd="http://www.w3.org/2001/XMLSchema">

    <xsd:element name="customer">
        <xsd:complexType>
            <xsd:sequence>
                <xsd:element 
                    name="phone-number" 
                    type="xsd:string" 
                    maxOccurs="unbounded"/>
            </xsd:sequence>
        </xsd:complexType>
    </xsd:element>

</xsd:schema>

Using the following command line:

xjc -d out customer.xsd

JAXB will generate the following class.  This class has a java.util.List property with java.util.ArrayList as the underlying implementation:

package generated;

import java.util.ArrayList;
import java.util.List;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;

@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "", propOrder = {"phoneNumber"})
@XmlRootElement(name = "customer")
public class Customer {

    @XmlElement(name = "phone-number", required = true)
    protected List<String> phoneNumber;

    public List getPhoneNumber() {
        if (phoneNumber == null) {
            phoneNumber = new ArrayList<String>();
        }
        return this.phoneNumber;
    }

}

Option #2 - Customizing the Generation

If you wish to control the underlying implementation you can use an external binding file.  We will use the bindings file below to change the underlying implementation to be java.util.LinkedList:

<jxb:bindings 
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    xmlns:jxb="http://java.sun.com/xml/ns/jaxb"
    version="2.1">

    <jxb:bindings schemaLocation="customer.xsd">
        <jxb:bindings node="//xs:element[@name='customer']/xs:complexType/xs:sequence/xs:element[@name='phone-number']">
            <jxb:property collectionType="java.util.LinkedList"/>
        </jxb:bindings>
    </jxb:bindings>

</jxb:bindings>

And the following XJC call:

xjc -d out -b binding.xml customer.xsd

To get the following class instead:

package generated;

import java.util.LinkedList;
import java.util.List;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;

@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "", propOrder = {"phoneNumber"})
@XmlRootElement(name = "customer")
public class Customer {

    @XmlElement(name = "phone-number", required = true)
    protected List phoneNumber = new LinkedList();

    public List getPhoneNumber() {
        if (phoneNumber == null) {
            phoneNumber = new LinkedList();
        }
        return this.phoneNumber;
    }

}

Option #3 - Using Your Own Domain Class

Generally you should interact with the java.util.List interface whenever possible.  However in your own domain classes you are free to make your properties any of the java.util.List implementations you wish (perhaps to make use of calls like java.util.ArrayList.trimToSize()).
package com.example;

import java.util.ArrayList;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;

@XmlAccessorType(XmlAccessType.FIELD)
@XmlRootElement(name = "customer")
public class Customer {

    @XmlElement(name = "phone-number", required = true)
    protected ArrayList phoneNumber;

    public Customer() {
        phoneNumber = new ArrayList();
    }

    public ArrayList getPhoneNumber() {
        return this.phoneNumber;
    }

}

2 comments:

  1. Hi Bliase,

    Thanks for putting information on customizing underlying List implementation. Just curious is there any alternative for JAXB available ?

    Thanks
    Javin
    FIX Protocol tutorial

    ReplyDelete
  2. Hi Javin,

    I'll begin by admitting my bias, that I lead EclipseLink JAXB (MOXy) and am a member of the JAXB 2 (JSR222) Expert Group.

    The best alternatives to JAXB are the individual JAXB implementations themselves (MOXy, Metro, JaxMe, etc). Each offers extensions that provide additional capabilities beyond the specification. Below are some of the MOXy extensions:

    - XPath Based Mapping
    - Representing Metadata as XML
    - Bidirectional Relationships
    - @XmlTransformation - Going Beyond XmlAdapter
    - CDATA Mapping

    There are other libraries that convert objects to/from XML. Here are some comparisons I put together:

    - JAXB and Simple
    - JAXB and XStream

    -Blaise

    ReplyDelete