Search This Blog

Wednesday 15 May 2013

JAXB and Complex types

In the previous post we saw how JAXB generated JAXBElement instances from simple types. Simple types however are just that - simple (!!!)
We often need our elements to allows attributes, include child elements etc. These cannot be achieved using simple types. We need to create complex types for the same.
Consider the below schema definition:
<element name="attributedAddress1">
 <complexType>
  <attribute name="city" type="string" use="optional"
   default="-" />
  <attribute name="zipcode" type="integer" use="required" />
  <attribute name="country" type="string" fixed="India" />
 </complexType>
</element>
From w3schools:
Simple elements cannot have attributes. If an element has attributes,
 it is considered to be of a complex type. But the attribute itself is 
always declared as a simple type.
The above definition is for an address element with three attributes - city, zipcode and country. If were to covert this to a java class:
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "")
@XmlRootElement(name = "attributedAddress1")
public class AttributedAddress1 {

 @XmlAttribute
 protected String city;
 @XmlAttribute(required = true)
 protected BigInteger zipcode;
 @XmlAttribute
 protected String country;

 public String getCity() {
  if (city == null) {
   return "-";
  } else {
   return city;
  }
 }

 public String getCountry() {
  if (country == null) {
   return "India";
  } else {
   return country;
  }
 }

 // other setter getters
}
Unlike simple elements a separate class was generated for the complex element. The annotation XmlRootElement is important here. From the java docs for the annotation:
When a top level class or an enum type is annotated with the @XmlRootElement 
annotation, then its value is represented as XML element in an XML document. 
The annotation causes an global element declaration to be produced in the 
schema. The global element declaration is associated with the XML schema type 
to which the class is mapped.
All the attributes in xsd have their corresponding class properties marked with the XmlAttribute annotation. The required property is set to true for zipcode, based on the value of the use attribute in xsd.
The getter methods for city and country are also interesting. For city we had specified the default value as "-". As a result the getter methods includes logic to return the default value if the attribute is not set. Similar logic was implemented in the country getter. This corresponds to the fixed attribute value in our xsd.
I decided to marshal the object to XML:
static void testMarshal() throws JAXBException {
 final ObjectFactory objectFactory = new ObjectFactory();
 final AttributedAddress1 address1 = objectFactory.createAttributedAddress1();
 address1.setZipcode(BigInteger.valueOf(100));
 
 final JAXBContext jContext = JAXBContext.newInstance(AttributedAddress1.class);
 final Marshaller marshaller = jContext.createMarshaller();

 final StringWriter stringWriter = new StringWriter();
 marshaller.marshal(address1, stringWriter);
 System.out.println(stringWriter.toString());
}
Here the interesting part is the JAXBContext creation. It is essential that JAXB is made aware of the class to be marshaled. This is not needed for simple elements or types. Here only the essential property of zipcode has been set.
On running the code, the xml generated is :
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<attributedAddress1 zipcode="100"
xmlns="http://test.com/xsd/simple/01/" />
This kind of raises the question - what is the difference between default and fixed ?
From w3 schools:
A default value is automatically assigned to the attribute when no 
other value is specified.
A fixed value is also automatically assigned to the attribute, and 
you cannot specify another value.
Unmarshal operation will similarly convert the xml into Java objects. Validations can be applied by providing a reference to the Schema, as we saw in the previous post.

No comments:

Post a Comment