JAXB is a Java binding tool. It generates Java code from a schema and you are able to transform from those classes into XML matching the processed schema and back. Note, that you cannot use your own objects, you have to use what is generated.In this post I will provide an alternative answer to that question.
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 XStream.
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.
XStream
The following code will be used to marshal the instance of Customer to an OutputStream. The XStream code is quite compact.
package comparison.xstream;
import com.thoughtworks.xstream.XStream;
import static comparison.Data.CUSTOMER;
public class XStreamDemo {
public static void main(String[] args) {
XStream xstream = new XStream();
xstream.autodetectAnnotations(true);
xstream.toXML(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:
- A JAXBContext needs to be initialized on the binding metadata before the marshal operation can occur.
- Unlike XStream JAXB does not format the XML by default, so we will enable this feature.
- 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);
}
}
<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>
@XmlAccessorType(XmlAccessType.FIELD) package comparison; import javax.xml.bind.annotation.XmlAccessType; import javax.xml.bind.annotation.XmlAccessorType;
import java.util.ArrayList;
import java.util.List;
import com.thoughtworks.xstream.annotations.XStreamAlias;
import com.thoughtworks.xstream.annotations.XStreamImplicit;
@XStreamAlias("customer")
public class Customer {
private long id;
private String name;
private Address address;
@XStreamImplicit(itemFieldName="phone-number")
private List phoneNumbers;
public Customer() {
phoneNumbers = new ArrayList();
}
}
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 phoneNumbers;
public Customer() {
phoneNumbers = new ArrayList();
}
}
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 XStream 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>
package comparison;
import java.util.ArrayList;
import java.util.List;
import com.thoughtworks.xstream.annotations.XStreamAlias;
import com.thoughtworks.xstream.annotations.XStreamAsAttribute;
import com.thoughtworks.xstream.annotations.XStreamImplicit;
@XStreamAlias("customer")
public class Customer {
@XStreamAsAttribute
private long id;
private String name;
private Address address;
@XStreamImplicit(itemFieldName="phone-number")
private List phoneNumbers;
public Customer() {
phoneNumbers = new ArrayList();
}
}
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 phoneNumbers;
public Customer() {
phoneNumbers = new ArrayList();
}
}
<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>
We can reference the converter on the phoneNumbers property using @XStreamConverter.package comparison;
import java.util.ArrayList;
import java.util.List;
import com.thoughtworks.xstream.annotations.XStreamAlias;
import com.thoughtworks.xstream.annotations.XStreamAsAttribute;
import com.thoughtworks.xstream.annotations.XStreamConverter;
import com.thoughtworks.xstream.annotations.XStreamImplicit;
@XStreamAlias("customer")
public class Customer {
@XmlAttribute
private long id;
private String name;
private Address address;
@XStreamImplicit(itemFieldName="phone-number")
@XStreamConverter(PhoneNumberConverter.class)
private List phoneNumbers;
public Customer() {
phoneNumbers = new ArrayList();
}
}
The converter can be implemented as follows:
package comparison;
import com.thoughtworks.xstream.converters.Converter;
import com.thoughtworks.xstream.converters.MarshallingContext;
import com.thoughtworks.xstream.converters.UnmarshallingContext;
import com.thoughtworks.xstream.io.HierarchicalStreamReader;
import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
public class PhoneNumberConverter implements Converter {
public boolean canConvert(Class clazz) {
return PhoneNumber.class == clazz;
}
public void marshal(Object object, HierarchicalStreamWriter hsw, MarshallingContext mc) {
PhoneNumber phoneNumber = (PhoneNumber) object;
hsw.addAttribute("type", phoneNumber.getType());
hsw.setValue(phoneNumber.getNumber());
}
public Object unmarshal(HierarchicalStreamReader hsr, UnmarshallingContext uc) {
PhoneNumber phoneNumber = new PhoneNumber();
phoneNumber.setType(hsr.getAttribute("type"));
phoneNumber.setNumber(hsr.getValue());
return phoneNumber;
}
}
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 XStream.
<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>
package comparison.xstream;
import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.converters.reflection.FieldDictionary;
import com.thoughtworks.xstream.converters.reflection.SortableFieldKeySorter;
import com.thoughtworks.xstream.converters.reflection.Sun14ReflectionProvider;
import com.thoughtworks.xstream.io.xml.QNameMap;
import com.thoughtworks.xstream.io.xml.StaxDriver;
import comparison.Address;
import static comparison.Data.CUSTOMER;
public class XStreamDemo {
public static void main(String[] args) {
QNameMap nsm = new QNameMap();
nsm.setDefaultNamespace("http://www.example.com");
StaxDriver staxDriver = new StaxDriver(nsm);
SortableFieldKeySorter sorter = new SortableFieldKeySorter();
sorter.registerFieldOrder(Address.class, new String[] { "street", "city"});
FieldDictionary fieldDictionary = new FieldDictionary(sorter);
Sun14ReflectionProvider reflectionProvider = new Sun14ReflectionProvider(fieldDictionary);
XStream xstream = new XStream(reflectionProvider, staxDriver);
xstream.autodetectAnnotations(true);
xstream.toXML(CUSTOMER, System.out);
}
}
Note: With this change I lost XML formatting, the following 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>
@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>
Nicely done.
ReplyDeleteBlaise,
ReplyDeletethanks for this comprehensive summary, really great.
One addition where I personally found JAXB to be superior to xstream is when you apply model transformations (as opposed to simple type conversions).
I think that JAXB's XmlAdapter is much simpler to deal with than xstream's custom mappers. I often see XSLT being used with xstream instead, but than you're using an additional language (and one which many java developers find cumbersome).
Thanks again,
Wolfgang
Wolfgang,
ReplyDeleteI agree on XmlAdapter, it is my favourite JAXB feature. It was actually the topic of my first blog post XmlAdapter - JAXB's Secret Weapon.
-Blaise
Excellent post, also note that JAXB now comes bundled on JDK, so the use of XStream gets a bit unneeded if the xml to be marshaled/unmarshaled are simple enough
ReplyDeletesombriks,
ReplyDeleteIn this example JAXB handled the XML better than XStream. This was especially true WRT namespace handling. For more information on JAXB's namespace support see:
- JAXB & Namespaces
XStream does have the converter concept, but JAXB has the much more powerful XmlAdapter feature:
- XmlAdapter - JAXB's Secret Weapon
Plus since JAXB is a spec, individual implementations can offer their own extensions such as MOXy's XPath based mapping:
- XPath Based Mapping
- XPath Based Mapping - Geocode Example
JAXB is not only a standard part of Java SE 6, but the default binding layer for two Java EE Web Service standards JAX-WS & JAX-RS. JAXB is the enterprise Java binding layer.
-Blaise
Blaise,
ReplyDeleteI guess the XStream FAQ primarily just needs an update for JAXB 2.0 ;-)
My experience with XStream is that it's great for serializing/deserializing arbitrary object graphs, but it's very hard to make the xml comply to some given schema. JAXB is at times more heavy-weight, but much more powerful as well.
But both by 95% serve the same purpose. I even started a little adapter class that uses XStream as an JAXB implementation... it's yet not much more than a proof of concept, though:
https://messageapi.dev.java.net/source/browse/messageapi/trunk/adapter/src/main/java/com/oneandone/consumer/messageapi/xstream
One thing that annoys me, though, is that the JDK "reference implementation" does not seem to comply to the standard paragraph 8.5.4, which says that Maps have to be supported. MOXy does, but the RI does not... or am I getting something wrong?
-Rüdiger
Rüdiger,
ReplyDeleteThe XStream FAQ definitely needs to be updated for JAXB 2.0 :).
To be fair XStream has times when it is more heavy weight. This can be seen in this post:
- A field dictionary had to be created to control the order of elements.
- A converter need to be created to map an object to simple content.
- The driver had to be changed to apply namespaces.
WRT 95%:
- XStream cannot generate classes from XML Schema. There FAQ recommends using XMLBeans for this purpose.
- XStream cannot generate an XML schema from Java classes
- XStream is not a Web Service binding layer.
- XStream does not cover 100% of XML schema concepts.
- XStream cannot preserve the XML infoset.
Section 8.5.4 of the JAXB spec clearly states that java.util.Map (and its subtypes) must be supported. This is probably a bug in the reference implementation, you can report this here:
- https://jaxb.dev.java.net/issues/
Don't forgot that JAXB implementations are free to go beyond the specification. Below are some of the EclipseLink MOXy extensions:
- XPath base mapping
- XML metadata file
- JPA entity mapping support
-Blaise
It looks like XStream can map inheritance better than JAXB.
ReplyDeletehttp://www.natpryce.com/articles/000766.html
Can JAXB do the same?
Andrew,
ReplyDeleteI read the article "Mapping Inheritance Cleanly with XStream", and it demonstrates how to write a converter to represent multiple classes as the same XML element. The JAXB equivalent would be to write an XmlAdapter.
- XmlAdapter - JAXB's Secret Weapon
The following posts demonstrate how to easily represent object inheritance in JAXB by leveraging existing XML schema concepts:
- JAXB and Inheritance - Using the xsi:type Attribute
- JAXB and Inheritance - Using Substitution Groups
-Blaise
Nice post. I was really happy with XStream, but JAXB looks cool for those cases also.
ReplyDeleteBy the way, int the XML output, in XStram you can "map" the qualified name to an alias: xstream.alias("customer", Customer.class);
Nice post. I too was under the impression that to use JAXB, you needed to start out with a schema, generate the java classes, etc. Was this ever the case? Either way, the tutorial you just went through needs to be front and center on Oracle's JAXB main page. It's great to know that JAXB can handle the hard stuff, but it's more important to get the word out how easy it is to use JAXB for the easy stuff. I mean, come on, one of the alternate libraries was probably named "Simple" for a reason. :)
ReplyDeleteThanks Ken,
ReplyDeleteJAXB 1 required that you start with an XML schema and generate standard interfaces backed by vendor specific implementation classes.
JAXB 2 moved to POJOs with standard annotations, making the classes portable between JAXB implementations. But somehow the stigma still persisted. It could be because JAXB still offers a schema to Java compiler to generate classes from an XML schema as a convenience mechanism.
I have also writted a post comparing Simple and JAXB. For that example they are both easy to use. The difference is in all the extra feaures that JAXB implementations (Metro, MOXy, JaxMe, etc) have:
- http://bdoughan.blogspot.com/2010/10/how-does-jaxb-compare-to-simple.html
-Blaise
Blaise,
ReplyDeleteThis is a very informative blog. I'm trying to decide between XStream and JAXB. It was useful to see that you don't have to use an XML schema with JAXB. I'm not a fan of annotations. I just don't like to pollute my code with it. Personal taste...
You can configure XStream to do your second example by registering aliases. Can this be done in JAXB without annotations? Also, can the examples in JAXB be done without using annotations? Are they required?
Hi Manuel,
ReplyDeleteJAXB as defined in the specification (JSR-222) requires annotations. However individual implementations may offer extensions to this.
EclipseLink MOXy (I'm the tech lead)
We offer an XML representation of the metadata. An example of it can be seen here:
- Extending JAXB - Representing Metadata as XML
Metro JAXB (The reference implementation)
Can leverage Annox and JAXBIntroductions to get an XML representation of the metadata. With a few lines of code MOXy can also be adapted to work with these formats.
- http://jaxb.java.net/
Other JAXB implementations such as JaxMe may address the use case in other ways.
One of the advantages of JAXB is that it is a standard with multiple implementations. This avoids the vendor lockin you get with a library like XStream.
-Blaise
Interesting post. I am after XML serialization/deserialization of Java object tree. Is there a way to do this using JAXB without tinkering with XmlAdapter or explicit mappings?
ReplyDeleteBlaise,
ReplyDeleteExcellent post!
I wonder if you can recommend an approach for handling evoloving classes. Class A, version1 is used at first. Then some of the clients update to Class A version2 (which introduces a new field). But other clients do not upgrade yet. They have the Class A version1 on their classpath. They should be able to read the xml representation of Class A version2 (by just ignoring the new field).
What's the best way to accomplish that? Can JAXB 'ignore' fields if the class does not have them? Can XStream? I saw that XMT can be used to read version1 with version2 code. But I'm talking about reading version2 with version1 code.
Any help would be great.
Hi Alex,
ReplyDeleteJAXB can be used to marshal an object to XML without the use of any metadata. JAXB metadata only needs to be supplied to override the default behavior. For an example see:
- http://wiki.eclipse.org/EclipseLink/Examples/MOXy/GettingStarted/TheBasics
-Blaise
Hi Anonymous,
ReplyDeleteRegarding evolving classes, JAXB handles this case easily. When JAXB finds an element/attribute it does not recognize it is simply ignored.
From the XStream FAQ if XStream finds an element/attribute it does not recognize an exception is thrown:
"If a field is removed from the class, deserializing an old version that contains that field will cause an exception."
-Blaise
Hmm, I looked at it some time ago, but at the time Java 6 bundled jaxb could not do the following:
ReplyDelete- If an object tree I serialize/deserialize has fields of interface types JAXB would not do it OOB
- If a field is declared as base class, but is assigned a subclass jaxb marshall/unmarshall looses this
- If I have multiple fields in the object tree that point to the same object instance, after jaxb unmarshalling they will become two independent instances.
- Java proxy serialization another issue
Is there an easy way to address all this without creating adapters?
Hi Alex,
ReplyDeleteThe JAXB specification does not define behavior for interfaces, but this is handled in EclipseLink JAXB (MOXy):
- MOXy JAXB - Map Interfaces to XML
JAXB does support inheritance. You need to ensure that the JAXBContext is aware of the subclasses:
- JAXB and Inheritance - Using the xsi:type Attribute
- JAXB and Inheritance - Using Substitution Groups
To handle shared references you do need to supply JAXB metadata to handle this:
- JAXB and Shared References: @XmlID and @XmlIDREF
For proxy serialization, refer to the post about mapping to interfaces.
-Blaise
Hi, what about performance comparison?
ReplyDeleteHi byF,
ReplyDeleteI do not have a performance comparison for JAXB and XStream. JAXB should be faster since if offers and initialization stage where the metadata can be optimized before unmarshalling/marshalling is done.
-Blaise
Hi, Blaise,
ReplyDeleteHave you used Jaxb in a OSGI/bundle environment? Do you know if it is supported on Felix?
I am facing a weird problem where the same code executes fine in a standalone java project, but complains that it does not see @XmlRootElement when this code runs in a bundle (in Felix) when it really is there.
Thanks in advance!
Hi Xianguan,
ReplyDeleteMost of the work I have done with JAXB and OSGi has been with Equinox.
In your manifest you will need to ensure that you are importing all the JAXB packages. You will also need to ensure that JAXB is available as a bundle. GlassFish provides one, and one is available in EclipseLink.
EclipseLink JAXB (MOXy) is also available as an OSGi bundle and can be obtained from the download site:
- http://www.eclipse.org/eclipselink/downloads/
-Blaise
thanks for the answer, Blaise. Really appreciate it.
ReplyDeleteDoes Xstream support multiple namespaces...??
ReplyDeleteWe can always set QnameMap.setDefaultNamespace() to set a default namesapce for the entire xml..but vat if I have to use more than one namespace in the same xml?
For specifics on how/if XStream handles multiple namespaces you will need to contact someone on that project.
ReplyDeleteJAXB implementations (Metro, EclipseLink MOXy, Apache JaxMe) have an elegant solution for this:
- JAXB & Namespaces
-Blaise
Hi there - Great information. WIth respect to speed, in my own testing I've seen JAXB outperform XStream on the order of 5 to 1. I'd be interested how it shook out with other peoples' use cases.
ReplyDeleteBlaZe, you are the man. This page helped me tremendously decide between JAXB and XStream. It has so much useful information (annotations esp.). I could totally build my marshalling strategy without creating any XSD etc...beautiful.
ReplyDeleteHi Amit,
ReplyDeleteI hope that means you chose JAXB :).
-Blaise
Hello Blaise
ReplyDeletePlease, as I implement the @xmlInverseReference of JAXB in XStream ?
Thanks
Hi Odete,
ReplyDeleteI am not aware if XStream has an equivalent of @XmlInverseReference. I'm the EclipseLink JAXB (MOXy) lead, and am more than happy to help you with MOXy (or any JAXB implementation), but I can't spend any cycles helping with XStream.
-Blaise
Hello Blaise,
ReplyDeleteIs there any possibility in JAXB to marshall all Object of a given ClassZ (e.g. the Date or a CustomClass) depending on some dynamic data (e.g date format, which changes for each object instance)?
With @XmlAdapter I will have to annotate all properties of type ClassZ with @XmlAdapter and constructing and adapter supposes other 2(or 3) classes.
The following may help:
ReplyDelete1. You can use the @XmlSchemaType at the field/property level to specify the XML representation of data:
- JAXB and Date/Time Properties
2. You can register an XmlAdapter for all classes of a particular type at the package level:
- JAXB and Joda-Time: Dates and Times
3. You can set initialized instances of XmlAdapter on Marshaller and Unmarshaller. The following answer I gave to a question on Stack Overflow demonstrates this concept in action:
- http://stackoverflow.com/questions/7278406/serialize-a-jaxb-object-via-its-id/7285517#7285517
-Blaise