March 8, 2011

Using JAXB to Implement a Copy Operation

The following post was inspired by an answer I gave to a question on Stack Overflow (feel free to up vote).  I am not suggesting that JAXB should be used to implement copy methods, instead I'm using a copy operation to demonstrate that JAXB can treat an object model as XML input.  


Java Model

The following domain model will be used for this example.  The only annotation I have used is @XmlRootElement.  Below I will describe how the copy operation can be performed with or without this annotation.

package blog.copy;

import javax.xml.bind.annotation.XmlRootElement;

@XmlRootElement
public class Customer {

    private String name;
    private Address address;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Address getAddress() {
        return address;
    }

    public void setAddress(Address address) {
        this.address = address;
    }

}

and

package blog.copy;

public class Address {

    private String street;
    private String city;

    public String getStreet() {
        return street;
    }

    public void setStreet(String street) {
        this.street = street;
    }

    public String getCity() {
        return city;
    }

    public void setCity(String city) {
        this.city = city;
    }

}

Demo Code - With @XmlRootElement


When the root object is annotated with @XmlRootElement the copy operation involves the following steps:

  1. Wrap the object to be copied in a JAXBSource
  2. Unmarshal the JAXBSource

package blog.copy;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import javax.xml.bind.util.JAXBSource;

public class Demo {

    public static void main(String[] args) throws Exception {
        // Build an instance of Customer with an Address
        Customer originalCustomer = new Customer();
        originalCustomer.setName("Jane Doe");
        Address originalAddress = new Address();
        originalAddress.setStreet("123 A Street");
        originalAddress.setCity("Any Town");
        originalCustomer.setAddress(originalAddress);

        // Create the JAXBContext
        JAXBContext jc = JAXBContext.newInstance(Customer.class);

        // Wrap the Customer object in a JAXBSource
        JAXBSource source = new JAXBSource(jc, originalCustomer);

        // Treat the JAXBSource as an XML input and unmarshal it
        Unmarshaller unmarshaller = jc.createUnmarshaller();
        Customer copiedCustomer = (Customer) unmarshaller.unmarshal(source);

        // Marshal the copy of Customer to demonstrate that it is populated
        Marshaller marshaller = jc.createMarshaller();
        marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
        marshaller.marshal(copiedCustomer, System.out);
    }

}

Demo Code - Without @XmlRootElement


When the root object is not annotated with @XmlRootElement the copy operation is slightly longer:
  1. Wrap the object to be marshalled in a JAXBElement
  2. Wrap the JAXBElement in a JAXBSource
  3. Unmarshal the JAXBSource passing the target class as a parameter


package blog.copy;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import javax.xml.bind.util.JAXBSource;
import javax.xml.namespace.QName;

public class JAXBElementDemo {

    public static void main(String[] args) throws Exception {
        // Build an instance of Customer with an Address
        Customer originalCustomer = new Customer();
        originalCustomer.setName("Jane Doe");
        Address originalAddress = new Address();
        originalAddress.setStreet("123 A Street");
        originalAddress.setCity("Any Town");
        originalCustomer.setAddress(originalAddress);

        // Create the JAXBContext
        JAXBContext jc = JAXBContext.newInstance(Customer.class);

        // Wrap the Customer object in a JAXBElement and a JAXBSource
        JAXBElement<Customer> originalJAXBElement = 
            new JAXBElement<Customer>(new QName("root"),Customer.class, originalCustomer);
        JAXBSource source = new JAXBSource(jc, originalJAXBElement);

        // Treat the JAXBSource as an XML input and unmarshal it
        Unmarshaller unmarshaller = jc.createUnmarshaller();
        JAXBElement<Customer> copiedJAXBElement = unmarshaller.unmarshal(source, Customer.class);
        Customer copiedCustomer = copiedJAXBElement.getValue();

        // Marshal the copy of Customer to demonstrate that it is populated
        Marshaller marshaller = jc.createMarshaller();
        marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
        marshaller.marshal(copiedJAXBElement, System.out);
    }

}
Further Reading


If you found this post useful, then you may also be interested in:

3 comments:

  1. Can this method also be used to copy a non-root element, address in your example? How would the procedure differ? Thanks.

    ReplyDelete
    Replies
    1. To apply this method to a non-root case you would need to wrap the object to be copied in a JAXBElement to provide root element information (for this purpose it would not matter what root element you used). Then on the unmarshal you would need to use an unmarshal method that takes a class parameter:
      - http://docs.oracle.com/javase/6/docs/api/javax/xml/bind/Unmarshaller.html#unmarshal(javax.xml.transform.Source,%20java.lang.Class)

      -Blaise

      Delete
  2. Thanks! This was helpful.

    ReplyDelete