October 25, 2010

How Does JAXB Compare to Simple?

In a previous post I compared JAXB to XStream.  In this post I'll run that comparison with the Simple XML Serialization framework.


Java Model

We will use the following model for this example.  The classes represent customer data.  The get/set methods have been omitted to save space.

package comparison;

import java.util.ArrayList;
import java.util.List;

public class Customer {

    private long id;
    private String name;
    private Address address;
    private List<PhoneNumber> phoneNumbers;

    public Customer() {
        phoneNumbers = new ArrayList<PhoneNumber>();
    }

}

package comparison;

public class Address {

    private String city;
    private String street;

}

package comparison;

public class PhoneNumber {

    private String type;
    private String number;

}

Customer Data

The following instance of Customer will be marshalled to XML using both JAXB and Simple.

package comparison;

public class Data {

    public static Customer CUSTOMER;

    static {
        CUSTOMER = new Customer();
        CUSTOMER.setId(123);
        CUSTOMER.setName("Jane Doe");

        Address address = new Address();
        address.setStreet("1 A Street");
        address.setCity("Any Town");
        CUSTOMER.setAddress(address);

        PhoneNumber workPhoneNumber = new PhoneNumber();
        workPhoneNumber.setType("work");
        workPhoneNumber.setNumber("555-WORK");
        CUSTOMER.getPhoneNumbers().add(workPhoneNumber);

        PhoneNumber cellPhoneNumber = new PhoneNumber();
        cellPhoneNumber.setType("cell");
        cellPhoneNumber.setNumber("555-CELL");
        CUSTOMER.getPhoneNumbers().add(cellPhoneNumber);
    }

}

Marshal Code

This is the code we will use to convert the objects to XML.

Simple 

The following code will be used to marshal the instance of Customer to an OutputStream.  The Simple code is quite compact.

package comparison.simplexml;

import org.simpleframework.xml.Serializer;
import org.simpleframework.xml.core.Persister;

import static comparison.Data.CUSTOMER;

public class SimpleXMLDemo {

    public static void main(String[] args) throws Exception {
        Serializer serializer = new Persister();
        serializer.write(CUSTOMER, System.out);
    }
}

JAXB

The following code will be used to marshal the instance of Customer to an OutputStream.  A couple of differences are already apparent:
  1. A JAXBContext needs to be initialized on the binding metadata before the marshal operation can occur.  This initialization enables JAXB to optimize how the conversion will be done.  The JAXBContext is thead safe and only needs to be created once.
  2. Unlike Simple, JAXB does not format the XML by default, so we will enable this feature.
  3. With no metadata specified we need to supply JAXB with a root element name (and namespace).
package comparison.jaxb;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.Marshaller;
import javax.xml.namespace.QName;
import comparison.Customer;

import static comparison.Data.CUSTOMER;

public class JAXBDemo {

    public static void main(String[] args) throws Exception {

        JAXBContext jc = JAXBContext.newInstance(Customer.class);

        Marshaller marshaller = jc.createMarshaller();
        marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
        JAXBElement<Customer> jaxbElement = new JAXBElement<Customer>(new QName("customer"), Customer.class, CUSTOMER);
        marshaller.marshal(jaxbElement, System.out);
    }
}

Default XML Output

First we will examine the XML output produced by both Simple and JAXB if no metadata is used to customize the output.

Simple

Simple will throw the following exception if no metadata has been defined.

Exception in thread "main" org.simpleframework.xml.transform.TransformException: Transform of class comparison.Customer not supported
 at org.simpleframework.xml.transform.Transformer.write(Transformer.java:122)
 at org.simpleframework.xml.core.Support.write(Support.java:200)
 at org.simpleframework.xml.core.PrimitiveFactory.getText(PrimitiveFactory.java:112)
 at org.simpleframework.xml.core.Primitive.write(Primitive.java:282)
 at org.simpleframework.xml.core.Composite.write(Composite.java:916)
 at org.simpleframework.xml.core.Traverser.write(Traverser.java:236)
 at org.simpleframework.xml.core.Traverser.write(Traverser.java:208)
 at org.simpleframework.xml.core.Traverser.write(Traverser.java:186)
 at org.simpleframework.xml.core.Persister.write(Persister.java:1187)
 at org.simpleframework.xml.core.Persister.write(Persister.java:1169)
 at org.simpleframework.xml.core.Persister.write(Persister.java:1147)
 at org.simpleframework.xml.core.Persister.write(Persister.java:1266)
 at org.simpleframework.xml.core.Persister.write(Persister.java:1248)
 at org.simpleframework.xml.core.Persister.write(Persister.java:1229)
 at comparison.simplexml.SimpleXMLDemo.main(SimpleXMLDemo.java:12)

We can enable Simple to apply default mappping rules by using the @Default annotation:

package comparison;

import java.util.ArrayList;
import java.util.List;
import org.simpleframework.xml.Default;

@Default
public class Customer {

    private long id;
    private String name;
    private Address address;
    private List<PhoneNumber> phoneNumbers;

    public Customer() {
        phoneNumbers = new ArrayList<PhoneNumber>();
    }

}

package comparison;

import org.simpleframework.xml.Default;

@Default
public class Address {

    private String city;
    private String street;

}

package comparison;

import org.simpleframework.xml.Default;

@Default
public class PhoneNumber {

    private String type;
    private String number;

}

Now Simple will produce the following XML:

<customer>
    <id>123</id>
    <name>Jane Doe</name>
    <address>
        <city>Any Town</city>
        <street>1 A Street</street>
    </address>
    <phoneNumbers class="java.util.ArrayList">
        <phoneNumber>
            <type>work</type>
            <number>555-WORK</number>
        </phoneNumber>
        <phoneNumber>
            <type>cell</type>
            <number>555-CELL</number>
        </phoneNumber>
    </phoneNumbers>
</customer>

JAXB

JAXB produces the following XML.  The representation is straight forward and more compact than the Simple representation.

<customer>
    <id>123</id>
    <address>
        <city>Any Town</city>
        <street>1 A Street</street>
    </address>
    <name>Jane Doe</name>
    <phoneNumbers>
        <number>555-WORK</number>
        <type>work</type>
    </phoneNumbers>
    <phoneNumbers>
        <number>555-CELL</number>
        <type>cell</type>
    </phoneNumbers>
</customer>

Field Access

For this example we will configure our XML binding tools to interact directly with the fields (instance variables).

Simple

Simple uses field access by default.

JAXB

By default JAXB will access public fields and properties.  We can configure JAXB to use field access with the following package level annotation:

@XmlAccessorType(XmlAccessType.FIELD)
package comparison;

import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;

Renaming Elements

Next we will look at how to tweak the XML output using the appropriate mapping metadata.  First we will rename some elements.  As you will see the amount of configuration required is almost identical.

Simple

For Simple the default root element is already what we want.  We will use @ElementList to configure the phoneNumbers property.

package comparison;

import java.util.ArrayList;
import java.util.List;

import org.simpleframework.xml.Default;
import org.simpleframework.xml.ElementList;

@Default
public class Customer {

    private long id;

    private String name;

    private Address address;

    @ElementList(inline=true, entry="phone-number")
    private List<PhoneNumber> phoneNumbers;

    public Customer() {
        phoneNumbers = new ArrayList<PhoneNumber>();
    }

}

JAXB

For JAXB we will use @XmlRootElement to configure the root element, and @XmlElement to configure the phoneNumbers property.
import java.util.ArrayList;
import java.util.List;

import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;

@XmlRootElement
public class Customer {

    private long id;
    private String name;
    private Address address;

    @XmlElement(name="phone-number")
    private List<PhoneNumber> phoneNumbers;

    public Customer() {
        phoneNumbers = new ArrayList<PhoneNumber>();
    }

}

Note:  The JAXB Marshal code can now be simplified since we no longer need to supply the root element information:

package comparison.jaxb;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.Marshaller;
import comparison.Customer;

import static comparison.Data.CUSTOMER;

public class JAXBDemo {

    public static void main(String[] args) throws Exception {

        JAXBContext jc = JAXBContext.newInstance(Customer.class);

        Marshaller marshaller = jc.createMarshaller();
        marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
        marshaller.marshal(CUSTOMER, System.out);
    }
}

XML Output

At this point the same XML is being produced by Simple and JAXB.

<customer>
    <id>123</id>
    <name>Jane Doe</name>
    <address>
        <city>Any Town</city>
        <street>1 A Street</street>
    </address>
    <phone-number>
        <type>work</type>
        <number>555-WORK</number>
    </phone-number>
    <phone-number>
        <type>cell</type>
        <number>555-CELL</number>
    </phone-number>
</customer>

Change the Order of Elements

We will tweak the document again to make sure that when marshalling an Address object the "street" element will always appear before the "city" element.

Simple

For Simple we will use @Order to configure the ordering of elements:

package comparison;

import org.simpleframework.xml.Default;
import org.simpleframework.xml.Order;

@Order(elements={"street", "city"})
public class Address {

    private String city;
    private String street;

}

JAXB

For JAXB we will use @XmlType to configure the ordering of elements.

package comparison;

import javax.xml.bind.annotation.XmlType;

@XmlType(propOrder={"street", "city"})
public class Address {

    private String city;
    private String street;

}

XML Output

The XML output is the same for both JAXB and Simple.

<customer>
    <id>123</id>
    <name>Jane Doe</name>
    <address>
        <street>1 A Street</street>
        <city>Any Town</city>
    </address>
    <phone-number>
        <type>work</type>
        <number>555-WORK</number>
    </phone-number>
    <phone-number>
        <type>cell</type>
        <number>555-CELL</number>
    </phone-number>
</customer>

Mapping to an Attribute

Now we will look at how to tweak the XML output using the appropriate mapping metadata to produce XML attributes.  As you will see the amount of configuration required is almost identical.

Simple

For Simple we will use @Attribute to configure the id property to be represented as an XML attribute.

package comparison;

import java.util.ArrayList;
import java.util.List;

import org.simpleframework.xml.Attribute;
import org.simpleframework.xml.Default;
import org.simpleframework.xml.ElementList;

@Default
public class Customer {

    @Attribute
    private long id;

    private String name;
    private Address address;

    @ElementList(inline=true, entry="phone-number")
    private List<PhoneNumber> phoneNumbers;

    public Customer() {
        phoneNumbers = new ArrayList<PhoneNumber>();
    }

}

JAXB

For JAXB we will use @XmlAttribute to configure the id property to be represented as an XML attribute.
package comparison;

import java.util.ArrayList;
import java.util.List;

import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;

@XmlRootElement
public class Customer {

    @XmlAttribute
    private long id;

    private String name;
    private Address address;

    @XmlElement(name="phone-number")
    private List<PhoneNumber> phoneNumbers;

    public Customer() {
        phoneNumbers = new ArrayList<PhoneNumber>();
    }

}

XML Output

The XML output is the same for both JAXB and Simple.
<customer id="123">
    <name>Jane Doe</name>
    <address>
        <city>Any Town</city>
        <street>1 A Street</street>
    </address>
    <phone-number>
        <type>work</type>
        <number>555-WORK</number>
    </phone-number>
    <phone-number>
        <type>cell</type>
        <number>555-CELL</number>
    </phone-number>
</customer>

Mapping Objects to Simple Content

To compact our document even further we will map the PhoneNumber class to a complex type with simple content.

Simple

With Simple we will use the @Attribute and @Text annotations on the PhoneNumber class.

package comparison;

import org.simpleframework.xml.Attribute;
import org.simpleframework.xml.Default;
import org.simpleframework.xml.Text;

@Default
public class PhoneNumber {

    @Attribute
    private String type;

    @Text
    private String number;

}
JAXB

For JAXB we will use the @XmlAttribute and @XmlValue annotations on the PhoneNumber class.

package comparison;

import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlValue;

public class PhoneNumber {

    @XmlAttribute
    private String type;

    @XmlValue
    private String number;

}

XML Output:

The XML output is the same for both JAXB and Simple.

<customer id="123">
    <name>Jane Doe</name>
    <address>
        <street>1 A Street</street>
        <city>Any Town</city>
    </address>
    <phone-number type="work">555-WORK</phone-number>
    <phone-number type="cell">555-CELL</phone-number>
</customer>

Applying Namespaces

We will now namespace qualify the XML document.

Simple

We will use the @Namespace annotation to specify a namespace for the document.  This may just be an issue of personal preference, but I do not like metadata on the Customer class affecting the namespace qualification on the Address and PhoneNumber classes.

package comparison;

import java.util.ArrayList;
import java.util.List;

import org.simpleframework.xml.Attribute;
import org.simpleframework.xml.Default;
import org.simpleframework.xml.ElementList;
import org.simpleframework.xml.Namespace;

@Default
@Namespace(reference="http://www.example.com")
public class Customer {

    @Attribute
    private long id;

    private String name;

    private Address address;

    @ElementList(inline=true, entry="phone-number")
    private List<PhoneNumber> phoneNumbers;

    public Customer() {
        phoneNumbers = new ArrayList<PhoneNumber>();
    }

}

JAXB

We can configure the namespace information using the @XmlSchema package level annotation:

@XmlAccessorType(XmlAccessType.FIELD)
@XmlSchema(namespace="http://www.example.com", 
           elementFormDefault=XmlNsForm.QUALIFIED)
package comparison;

import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlNsForm;
import javax.xml.bind.annotation.XmlSchema;

The following XML was produced

<customer xmlns="http://www.example.com" id="123">
    <name>Jane Doe</name>
    <address>
        <street>1 A Street</street>
        <city>Any Town</city>
    </address>
    <phone-number type="work">555-WORK</phone-number>
    <phone-number type="cell">555-CELL</phone-number>
</customer> 
 

Summary

It is easy to do simple things with Simple.  As can be seen from this example it is also easy to do simple things with JAXB.  I prefer JAXB's version of the annotations, but since I helped design them I will admit my bias.  However JAXB does have several advantages:
  • JAXB is a standard with multiple implementations:  Metro, EclipseLink MOXy, JaxMe.
  • An implementation of JAXB is included in Java SE 6.
  • JAXB has annotations to cover all the remaining XML Schema concepts.
  • JAXB is the default Web Service binding layer for JAX-WS and JAX-RS.

Further Reading

If you enjoyed this post you may also be interested in:

    19 comments:

    1. I find the package level annotations awkward to look at. Is there another way to achieve the same result?

      ReplyDelete
    2. In this example I set a couple of JAXB annotations at the package level. This can be done by using a class named package-info.

      @XmlAccessorType
      The @XmlAccessorType can also be set at the type level. To eliminate its use all together simply annotate the public get (or set) method instead of the private field.

      @XmlSchema
      JAXB has support for specifying namespace information at the package, type, and property/field levels.

      Alternatively MOXy offers the ability to represent the JAXB metadata as an XML file:

      - http://wiki.eclipse.org/EclipseLink/Examples/MOXy/EclipseLink-OXM.XML

      ReplyDelete
    3. I've used Simple and one of the nice things about it is the ability to use polymorphisim via fields or property methods, for example.

      @ElementList
      private List list = new MyListType()

      Can JAXB figure out a list assignment like this? If so does it require an annotation with a specific type to use?

      ReplyDelete
    4. Anonymous,

      Simple

      If we change the Simple @ElementList annotation we used on the "phoneNumbers" property to be the following @ElementList(entry="phone-number"), then a grouping element will be created that looks like the following:

      <phoneNumbers class="java.util.ArrayList">

      I presume this means that the when unmarshalling the XML the java.uti.List property should be populated with an instance of java.util.ArrayList.

      For this type of mechanism to work, the following needs to be true:
      - Both the object used to produce XML, and the object used to consume the XML both need to use ArrayList for that value.
      - If there is an XML schema associated with this XML it needs to account for the class attribute.

      JAXB

      If you initialize a property with a particular implementation of list then JAXB will use that initialized list. If one was not pre-initialized an implementation will be chosen for you (both Metro and MOXy will use ArrayList). In EclipseLink 2.2, MOXy will offer the ability to specify List implementation in its XML version of the metadata.

      -Blaise

      ReplyDelete
    5. Nice tutorial

      I have one question.
      Is there a way to marshall/unmarshall a collection of objects without having to create a new java wrapper class.

      Regards

      ReplyDelete
    6. Currently you will need to use a wrapper class to handle this use case. I have entered an enhancement request against MOXy for this issue (see below). Feel free to add any additional details to the request that you feel are useful for us to consider:

      - https://bugs.eclipse.org/328862

      ReplyDelete
    7. I am new to JAXB and simple but found this useful.


      I like the fact that for validation, JAXB is led by XSD. My understanding of simple is that you really have to re-write your XSD constraints into your annotations to get the validation of the XML. That's a big weakness, if my understanding is right.

      However, I liked the fact that in simple, I was not forced to having a no-arg constructor. I want immutable classes. I liked having a constructor which looked like this


      public XmlMessage(@Attribute(name="v", required=true) int version,
      @Element(name="sc") Script script,
      @Element(name="pa") Patient patient,
      @Element(name="pb") Prescriber prescriber,
      @ElementList(name="drugs",inline=true) List drugs ) {
      super();
      this.version = version;
      this.script = script;
      this.patient = patient;
      this.prescriber = prescriber;
      this.drugs = drugs;
      }

      My work with JAXB so far seems to not allow this type of thing.

      ReplyDelete
    8. Christopher,

      With JAXB you can optionally register an instance of javax.xml.validation.Schema with both the marshaller and unmarshaller to validate against an XSD.

      JAXB requires a no-arg constructor. You can workaround this issue through the use of XmlAdapter. I will post an example of how to do this on my blog soon.

      I have entered the following enhancement request to cover support for multi-arg constructors. Feel free to add any information you feel is relevant.

      - https://bugs.eclipse.org/328951

      -Blaise

      ReplyDelete
    9. Hi All,
      regarding the case of using extra attribute like the "class" from the simpleXml it could be solved by using a Visitor implementation that can inspect the serilization process and remove any un-wantred field.
      But I have a small question, can we in JAXB, SimpleXML de-serilize an XML even if the case differs (case in-sensitive)?

      ReplyDelete
    10. Hi BODYLANG,

      Agreed a visitor could be used to remove unwanted field, but since Simple XML markets itself as being simple, extra steps count against it there.

      However you could use extra steps to support case insensitive mapping in JAXB (and probably Simple XML). You could map all your properties to lower case node names, and then wrap an XMLStreamReader to call toLowerCase() on all attribute/element names it returned.

      I have added an enhance request for EclipseLink JAXB (MOXy) for this issue, feel free to provide additional information:

      - https://bugs.eclipse.org/331241

      -Blaise

      ReplyDelete
    11. Hi BODYLANG,

      I have posted an example on how case insensitive unmarshalling could be done using JAXB today:

      - Case Insensitive Unmarshalling with JAXB

      -Blaise

      ReplyDelete
    12. Hi,

      This looks useful, however I think it works a little better in Simple. You can simply style the attributes and elements with a Style object, this avoids, which means you get transparent serialization and deserialization regardless of case.

      It would be good to support a feature that can intercept the marshalling and unmarshalling and adapt the names transparently.

      Tom

      ReplyDelete
    13. Hi Tom,

      Thanks for the suggestion. I have entered the following enhancement request for EclipseLink MOXy:

      - Enhancement: Customizable Name Mangling Algorithm

      -Blaise

      ReplyDelete
    14. My colleague says that the XML serialisation and deserialisation with Simple is inefficient, only writing/reading a few bytes at a time to/from disk. According to him, it looks like the serialisation writes one attribute at a time. The deserialisation is even worse, reading a single byte at a time from disk.

      Is this claim true?

      Bala

      ReplyDelete
    15. Hi Bala,

      I can't speak to how Simple is implemented. In EclipseLink JAXB (MOXy) we leverage a java.io.BufferedWriter to solve this problem for writing.

      -Blaise

      ReplyDelete
    16. Im happy with JAXB but now I need to write something on Android, JAXB isnt suitable for this but SimpleXml is. Do you of any tool that could convert JAXB clases to Simple ones ?

      ReplyDelete
    17. Hi Paul,

      I am not aware of any such tool. As the EclipseLink JAXB (MOXy) lead, I have not investigated the existence of such a tool. However, I'm more than happy to help people port form SimpleXML to JAXB.

      -Blaise

      ReplyDelete
      Replies
      1. Thanks for the info, I am trying to convert some jaxb objects to simpleXML, and having hard time to convert this tag

        @XmlTransient
        @XmlElementWrapper(name = "MyContentList") @XmlElementRefs({@XmlElementRef(type=video.class,name="video"), @XmlElementRef(type=audio.class,name="audio")})

        Thanks

        Delete
    18. Refering to same question asked by Paul Taylor, is there any tool to convert the JAXB class to SimpleXML classes?

      ReplyDelete

    Note: Only a member of this blog may post a comment.