How Does JAXB Compare to XStream?
The XStream FAQ states the following when being compared to JAXB:
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.
Customer Data
The following instance of Customer will be marshalled to XML using both JAXB and XStream.
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.
JAXB
The following code will be used to marshal the instance of Customer to an OutputStream. A couple of differences are already apparent:
Note: The JAXB Marshal code can now be simplified since we no longer need to supply the root element information:
XML Output
At this point the same XML is being produced by XStream and JAXB.
We can reference the converter on the phoneNumbers property using @XStreamConverter.
The converter can be implemented as follows:
JAXB
For JAXB we will use the @XmlAttribute and @XmlValue annotations on the PhoneNumber class.
XML Output:
The XML output is the same for both JAXB and XStream.
Note: With this change I lost XML formatting, the following was produced.
The following XML was produced
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.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | 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>(); } } </PHONENUMBER> |
1 2 3 4 5 6 7 8 | package comparison; public class Address { private String city; private String street; } |
1 2 3 4 5 6 7 8 | 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.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | 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.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | 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).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | 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); } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | < 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 > |
1 2 3 4 5 | @XmlAccessorType (XmlAccessType.FIELD) package comparison; import javax.xml.bind.annotation.XmlAccessType; import javax.xml.bind.annotation.XmlAccessorType; |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | 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<PHONENUMBER> phoneNumbers; public Customer() { phoneNumbers = new ArrayList<PHONENUMBER>(); } } </PHONENUMBER></PHONENUMBER> |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | 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>(); } } </PHONENUMBER></PHONENUMBER> |
Note: The JAXB Marshal code can now be simplified since we no longer need to supply the root element information:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | 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.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | < 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 > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | 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<PHONENUMBER> phoneNumbers; public Customer() { phoneNumbers = new ArrayList<PHONENUMBER>(); } } </PHONENUMBER></PHONENUMBER> |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | 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>(); } } </PHONENUMBER></PHONENUMBER> |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | < 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 > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | 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<PHONENUMBER> phoneNumbers; public Customer() { phoneNumbers = new ArrayList<PHONENUMBER>(); } } </PHONENUMBER></PHONENUMBER> |
The converter can be implemented as follows:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | 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.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | 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.
1 2 3 4 5 6 7 8 9 | < 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 > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | 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(); 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.
1 2 | < 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 > |
1 2 3 4 5 6 7 8 9 | @XmlAccessorType (XmlAccessType.FIELD) 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
1 2 3 4 5 6 7 8 9 | < 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 > |
No comments:
Post a Comment