Login | Register   
LinkedIn
Google+
Twitter
RSS Feed
Download our iPhone app
TODAY'S HEADLINES  |   ARTICLE ARCHIVE  |   FORUMS  |   TIP BANK
Browse DevX
Sign up for e-mail newsletters from DevX


advertisement
 

Generate an XML Document from an Object Model with JAXB 2 : Page 3

Learn how to get an XML document from your Java object model using JAXB 2. With annotations, you can also customize the document and use adapters to format data.


advertisement
Customize the XML Document
Watermelon and its business partner are not completely happy with the structure of the previous XML document (Listing 2). They would like to get rid of some information (address identifiers, tags), format the date of birth, order some attributes, and rename others. Thanks to the annotations of the javax.xml.bind.annotation package, JAXB provides a way to customize and control the XML structure.

First of all, you don't really want to map customers. Rather, you want to map individuals and companies (that means getting rid of the <customer> element and instead using <individual> or <company> as root elements). To notify JAXB not to take the abstract Customer class into account, you have to get rid of the @XmlRootElement annotation and make the class transient with @XmlTransient. You can use this annotation on a class or an attribute. It informs JAXB not to take it into account when creating the XML representation.

An XML document is made of elements (<element>value</element>) and attributes (<element attribute="value"/>). JAXB uses two annotations to differentiate them: @XmlAttribute and @XmlElement. Each annotation has a set of parameters that allows you to rename an attribute, allow null value or not, give it a default value, etc. The following code uses these annotations to turn id into an XML attribute (instead of an element) and to rename the delivery address element (address instead of deliveryAddresses):



@XmlTransient public abstract class Customer { @XmlAttribute protected Long id; protected String telephone; protected String email; protected Address homeAddress; @XmlElementWrapper(name = "delivery") @XmlElement(name = "address") protected List<Address> deliveryAddresses = new ArrayList<Address>(); // Constructors, getters, setters }

This code uses another interesting annotation, @XmlElementWrapper. It generates a wrapper element around the collections of delivery addresses. If you look back at Listing 2, you'll see two <deliveryAddresses> elements. With the code above, you get one <delivery> element that wraps two <address> elements.

Talking about addresses, you want to get rid of the identifier and the tags from the XML document. For that, use the @XmlTransient annotation. To rename an element, just use the name property of the @XmlElement annotation. The following code renames the attribute zipcode into a <zip> element:

@XmlType(propOrder = {"street", "zipcode", "city", "country"}) @XmlAccessorType(XmlAccessType.FIELD) public class Address { @XmlTransient private Long id; private String street; private String city; @XmlElement(name = "zip") private String zipcode; private String country; @XmlTransient private List<Tag> tags = new ArrayList<Tag>(); // Constructors, getters, setters }

Notice the @XmlType annotation on the top of the class. It allows JAXB to map a class or an enum to a XML schema type. You can use it to specify a namespace or to order attributes using the propOrder property, which takes a list of names of attributes and generates the XML document following this order.

Author's note: The Address class is annotated with @XmlAccessorType. It controls whether fields or Java bean properties are serialized. XmlAccessType represents the possible values (FIELD, NONE, PROPERTY, PUBLIC_MEMBER). This example uses the FIELD value, which notifies JAXB to use the attributes for the XML mapping (and not getters).

Table 1 below shows three different extracts of XML documents:

  1. The default customer XML document that you would get with coding-by-exceptions (with no annotation except @XmlRootElement)
  2. The customizations done to the customer class (the identifier becomes an attribute and the delivery addresses are wrapped into a <delivery> element)
  3. Taken into account the annotations of the Address class (no identifier and no tags are displayed; elements are in a certain order)

Default XML Representation Annotated Customer Class Annotated Address Class
<customer>
  <deliveryAddresses>

    <city>London</city>
    <country>UK</country>
    <id>3</id>
    <street>Findsbury</street>
    <tags>
      <name>working hours</name>
    </tags>
    <tags>
      <name>mind the dog</name>
    </tags>
    <zipcode>CE451</zipcode>
  </deliveryAddresses>
  <deliveryAddresses>

    <city>Brighton</city>
    <country>UK</country>
    <id>4</id>
    <street>Camden</street>
    <tags>
      <name>working hours</name>
    </tags>
    <tags>
      <name>week-ends</name>
    </tags>
    <zipcode>NW487</zipcode>
  </deliveryAddresses>
  (...)
</customer>
<individual id="1">
  <delivery>
    <address>

      <city>London</city>
      <country>UK</country>
      <id>3</id>
      <street>Findsbury</street>
      <tags>
        <name>working hours</name>
      </tags>
      <tags>
        <name>mind the dog</name>
      </tags>
      <zipcode>CE451</zipcode>
    </address>
    <address>

      <city>Brighton</city>
      <country>UK</country>
      <id>4</id>
      <street>Camden</street>
      <tags>
        <name>working hours</name>
      </tags>
      <tags>
        <name>week-ends</name>
      </tags>
      <zipcode>NW487</zipcode>
    </address>
  </delivery>
  (...)
</individual>
<individual id="1">
  <delivery>
   <address>

     <street>Findsbury</street>
     <zip>CE451</zip>
     <city>London</city>
     <country>UK</country>
   </address>
   <address>

     <street>Camden</street>
     <zip>NW487</zip>
     <city>Brighton</city>
     <country>UK</country>
   </address>
  </delivery>
  (...)
</individual>
Table 1. Customizations of the XML Representation of a Customer

You also can annotate the concrete classes Company and Individual to customize the mapping (see table below). First of all, being the root of the XML document now, both have to use the @XmlRootElement annotation where they can specify the XML namespace http://www.watermelon.example/customer. Like the Address, the example uses the @XmlType.propOrder to order the attributes. Note that in the list of attributes, you can use the ones inherited by the super class Customer, such as id, email, telephone, homeAddress, and so on.

Annotated Company Class Annotated Individual Class
@XmlRootElement(name = "company", namespace =
        "http://www.watermelon.example/customer")
@XmlType(propOrder = {"id", "name", "contactName",
        "telephone", "email", "numberOfEmployees",
        "homeAddress", "deliveryAddresses"})
@XmlAccessorType(XmlAccessType.FIELD)

public class Company extends Customer {

  @XmlAttribute
  private String name;
  private String contactName;
  private Integer numberOfEmployees;
  // Constructors, getters, setters
}
@XmlRootElement(name = "individual", namespace =
        "http://www.watermelon.example/customer")
@XmlType(propOrder = {"id", "lastname",
         "firstname", "dateOfBirth", "telephone",
         "email", "homeAddress",
         "deliveryAddresses"})
@XmlAccessorType(XmlAccessType.FIELD)

public class Individual extends Customer {

  private String firstname;
  @XmlAttribute
  private String lastname;
  @XmlJavaTypeAdapter(DateAdapter.class)
  private Date dateOfBirth;
  // Constructors, getters, setters
}

There is still one thing that Watermelon is not happy about: the date format. JAXB maps java.util.Date attributes into the default value. For example, the date of birth of an individual will be displayed as follow:

<dateOfBirth>1940-08-07T00:00:00.781+02:00</dateOfBirth>

To format this date (e.g., 07/08/1940), you have two choices:

  1. Use the datatype javax.xml.datatype.XMLGregorianCalendar instead of java.util.Date. The XMLGregorianCalendar gives a W3C XML representation for date/time datatypes and allows certain manipulations.
  2. Use an adapter. As you can see in the code above, the Individual class uses a @XmlJavaTypeAdapter annotation. @XmlJavaTypeAdapter(DateAdapter.class) notifies JAXB to use the custom adapter called DateAdapter when marshalling/unmarshalling the dateOfBirth attribute. Adapters are used when Java types do not map naturally to a XML representation. You can then adapt a bound type to a value type or vice versa.

To format the date, you have to write a class (DateAdpater) that extends XmlAdapter. It has to override the marshal and unmarshal methods, which the JAXB binding framework invokes during marshalling and unmarshalling. This way, you can marshal a date into a formatted string and vice versa. The following code just uses a java.text.SimpleDateFormat object to format the date into a string and vice-versa:

public class DateAdapter extends XmlAdapter<String, Date> { DateFormat df = new SimpleDateFormat("dd/MM/yyyy"); public Date unmarshal(String date) throws Exception { return df.parse(date); } public String marshal(Date date) throws Exception { return df.format(date); } }

Now if you go back to Listing 1, when you call the Marshaller.marshal() method, the DateAdapter.marshal() is also called (@XmlJavaTypeAdapter(DateAdapter.class)) and the date of birth is then formatted. Here is the XML document you obtain:

Individual XML Document
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<ns2:individual lastname="Starr" id="1" xmlns:ns2="http://www.watermelon.example/customer">
    <firstname>Ringo</firstname>
    <dateOfBirth>07/08/1940</dateOfBirth>
    <telephone>+187445</telephone>
    <email>ringo@star.co.uk</email>
    <homeAddress>
        <street>Abbey Road</street>
        <zip>SW14</zip>
        <city>London</city>
        <country>UK</country>
    </homeAddress>
    <delivery>
        <address>

            <street>Findsbury Avenue</street>
            <zip>CE451</zip>
            <city>London</city>
            <country>UK</country>
        </address>
        <address>

            <street>Camden Street</street>
            <zip>NW487</zip>
            <city>Brighton</city>
            <country>UK</country>
        </address>
    </delivery>
</ns2:individual>
Company XML Document
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<ns2:company name="Sony" id="1" xmlns:ns2="http://www.watermelon.example/customer">
    <contactName>Mr Father</contactName>
    <telephone>+14519454</telephone>
    <email>contact@sony.com</email>
    <numberOfEmployees>25000</numberOfEmployees>
    <homeAddress>
        <street>General Alley</street>
        <zip>75011</zip>
        <city>Paris</city>
        <country>FR</country>
    </homeAddress>
    <delivery>
        <address>

            <street>St James St</street>
            <zip>SW14</zip>
            <city>London</city>
            <country>UK</country>
        </address>
        <address>

            <street>Central Side Park</street>
            <zip>7845</zip>
            <city>New York</city>
            <country>US</country>
        </address>
    </delivery>
</ns2:company>



Comment and Contribute

 

 

 

 

 


(Maximum characters: 1200). You have 1200 characters left.

 

 

Sitemap