A Standalone JAX-B Example

Recall that the B in JAX-B stands for data binding, the associating of a Java data type such as String to an XML Schema (or equivalent) type, in this case xsd:string. There are built-in bindings for the Java primitive types such as int and double together with String and Calendar; arrays (including Collections) of any such types; and programmer-defined types that reduce, via properties, to any of the preceding. The surprising omission is the Map, a collection of key/value pairs, but a Map is readily handled as two coordinated collections: a collection of keys and a corresponding collection of values. An example of JAX-B in action may help to drive these points home.

The Skier class (see Example 3-4) is annotated with @XmlRootElement to inform the JAX-B utilities that a Skier instance should be transformed into an XML document that has skier as its root or document (that is, outermost) element. In the default Java naming convention, the root element is the lowercase version of the class name; hence, Skier becomes skier. The annotation could be amended:

@XmlRootElement(name = "NordicSkier")

so that the root element has a specified name, in this example NordicSkier.

Example 3-4. The annotated Skier POJO class

import javax.xml.bind.annotation.XmlRootElement;
import java.util.Collection;

@XmlRootElement
public class Skier  {
    private Person person;
    private String nationalTeam;
    private Collection majorAchievements;
    public Skier() { } // required for unmarshaling
    public Skier (Person person,
                  String nationalTeam,
                  Collection<String> majorAchievements) {
        setPerson(person);
        setNationalTeam(nationalTeam);
        setMajorAchievements(majorAchievements);
    }
    // properties
    public Person getPerson() { return this.person; }
    public void setPerson (Person person) { this.person = person; }

    public String getNationalTeam() { return this.nationalTeam; }
    public void setNationalTeam(String nationalTeam) {
        this.nationalTeam = nationalTeam;
    }
    public Collection getMajorAchievements() { return this.majorAchievements; }
    public void setMajorAchievements(Collection majorAchievements) {
        this.majorAchievements = majorAchievements;
    }
}

The Skier class has a property of programmer-defined type Person (see Example 3-5), which in turn is a POJO class with three properties: name, age, and gender. Two of the Person properties are of Java type String, which binds to XML type xsd:string. The third Person property is of Java type int, which binds to the XML type xsd:int.

Example 3-5. The annotated Person POJO class with three properties

import javax.xml.bind.annotation.XmlType;

@XmlType
public class Person  {
    private String name;
    private int    age;
    private String gender;

    public Person() { }
    public Person(String name, int age, String gender){
        setName(name);
        setAge(age);
        setGender(gender);
    }
    public String getName() { return name; }
    public void setName(String name) { this.name = name; }
    public int getAge() { return age;  }
    public void setAge(int age) { this.age = age; }
    public String getGender() { return gender; }
    public void setGender(String gender) { this.gender = gender; }
}

The annotation XmlType declares that a Person instance can be transformed into an XML type, a type that an XML Schema specifies in detail. The upshot of the annotated Skier and Person classes is this: an in-memory Skier object, which encapsulates a Person, can be transformed into a single XML document, whose root element is tagged skier, and the skier document encapsulates a person element.

The application class Marshal (see Example 3-6) does the following:

Recall that, in this context, marshaling is the process of serializing an in-memory object into an XML document; unmarshaling is the inverse process of creating an in-memory object from an XML document.

Example 3-6. The Marshal application that marshals and unmarshals a Skier

In the Marshal class, the critical step is the creation of a JAXBContext (line 1), in this case a structure built from Java reflection on the type Skier. (The one-argument method newInstance can take, as its argument, a single class or a package identifier.) The utility class JAXBContext then guides the marshaling and unmarshaling: the Marshaller and the Unmarshaller are created with JAXBContext methods (lines 2 and 4). By the way, there is no agreement about whether marshaling and unmarshaling should be spelled with one l or two.

The marshaling (line 3) produces an XML document (see Example 3-7) that serves as the source of the unmarshaling (line 4). The only complexity is in the elements tagged majorAchievement, which include three attributes apiece. The reason is that a majorAchievement is, in Java, a Collection type—in particular, an ArrayList<String>. The corresponding XML type is an array of xs:string objects; the majorAchievement elements cite the XML Schema grammar (lines 1 and 2), which includes rules about arrays. By default, the Java marshaling produces an XML document in which the properties of the source Java object are in alphabetical order.

Example 3-7. The XML document generated from marshaling a sample Skier

This JAX-B example implies, of course, that the conversion between Java and XML could be automated. Accordingly, a Java client against a RESTful service could:

In this scenario, the XML is transparent. The second Amazon example goes into the details of how this can be done. First, however, an alternative to the standard Java XML utilities deserves a look.

Java comes with standard packages for Java-to-XML conversions and XML-to-JSON conversions. There are also various contributed libraries, among them XStream. This section examines the XStream option for serializing Java objects to XML/JSON documents and deserializing Java objects from such documents. XStream includes a persistence API and has extensions in support of the Hibernate ORM (Object Relation Mapper). Among the more interesting features of XStream is that its API does not center on the get/set methods that define Java properties. XStream can serialize into XML an instance of a Java class that has nothing but private fields. XStream emphasizes its ease of use, which the following examples try to capture.

The PersonNoProps class (see Example 3-8) illustrates the ease of XStream use. The class has three private fields and only a three-argument constructor; of interest is that the class has no properties—no public get/set methods. Nonetheless, an instance of the PersonNoProps class can be serialized or deserialized straightforwardly. Line 1 in the code listing constructs an XStream instance with a DOM driver. Line 2 provides an alias for the document element’s tag, in this case skier. If an alias were not provided, then the document element would have a tag named after the class; hence, line 2 is optional. The serialization in line 3 and the deserialization in line 4 are quick and easy.

The output for both println calls is:

<skier>
  <name>Bjoern Daehlie</name>
  <age>49</age>
  <gender>Male</gender>
</skier>

The generated XML is minimalist. Compiling and running this code requires the core XStream packages, which come in a single JAR file: xstream.jar.

The first XStream example begins with the serialization of an entire PersonNoProps instance and ends with the deserialization of a clone. XStream also supports selective or fine-grained serialization and deserialization. The next example (see Example 3-9) illustrates this.

The PersonPropsConverter class (see Example 3-9) serializes and deserializes, as proof of concept, only one property in a PersonProps instance: the name property. An implementation of the Converter interface must define three methods:

canConvert
This method (line 1) returns a boolean to indicate which types are eligible for the customized serialization and deserialization defined in the methods marshal and unmarshal (lines 2 and 3, respectively). In this example, an object must be of type PersonProps (see Example 3-10), which includes any descendants of this class, in order to be convertible.
marshal
This method (line 2) supports customized serialization (marshaling) of a PersonProps object. In the current example, only the person’s name property is serialized, but any subset of the properties, including all of them, could be serialized.
unmarshal
This method (line 3), the inverse of marshal, supports customized deserialization (unmarshaling).

The PersonProps class (see Example 3-10) revises the PersonNoProps class by adding properties. The revised class has three conventional Java properties, each defined as a pair of get/set methods. The properties are name (type String) in line 1, age (type int) in line 2, and gender (type String) in line 3. The revised class has a no-argument constructor.

The tester class Main creates an XStream instance as before but now registers a customized Converter (line 4), a PersonPropsConverter (see Example 3-9). In the call to toXML (line 5), the customized converter takes over and serializes only the name property. The output is:

<person>
  <name>Bruno</name>
</person>

The deserialization (line 6) creates a new PersonProps instance and sets the name property to Bruno. The other properties, age and gender, have the default values for fields, in this case 0 and null, respectively.

The core XStream library also supports the conversion of Java objects to and from JSON. There are various JSON drivers available in this library, the simplest of which is the JsonHierarchicalStreamDriver. This driver supports the serialization of Java objects to JSON but not the inverse operation. If deserialization from JSON to Java is needed, then a driver such as Jettison is a good choice because it interoperates cleanly with XStream.

The JsonTest code (see Example 3-11) illustrates basic JSON serialization in XStream. An XStream instance is now constructed with a JSON driver (line 1), in this case an instance of a JsonHierarchicalStreamDriver, which comes with the core XStream JAR file. The serializing method is still named toXML (line 2), but the output is JSON rather than XML because of the JSON driver.

Here is the output:

{"PersonNoProps": {
  "name": "Bjoern Daehlie",
  "age": 49,
  "gender": "Male"
}}

XStream supports customized JSON serialization. For example, a programmer might not want the root element PersonNoProps included in the JSON, and the JSON serializer can be programmed to exclude this element.

The XStream API is remarkably low fuss but likewise powerful. This API has gained steadily in popularity among Java developers who are looking for quick and easy ways to convert between Java objects on the one side and either XML or JSON documents on the other side.

The JAX-B and XStream examples illustrate serialization from Java to XML or JSON and deserialization from XML or JSON to Java. In the context of clients against RESTful web services, the deserialization side of the coin is of primary interest because these clients need to process the response payloads, in XML or JSON, that come from the RESTful service. Accordingly, the next section returns to Amazon’s E-Commerce service but this time with the goal of hiding the XML that this service returns in response to a successful HTTP request.