March 28, 2013

MOXy's Object Graphs - Partial Models on the Fly to/from XML & JSON

In a previous post I introduced how MOXy's Object Graph feature allows you to input and output partial models.  In that example I demonstrated how to define a partial model via metadata.  In this example we will create the object graphs programmatically.  This gives you additional flexibility when you need to create one on the fly.

You can try this out today by downloading an EclipseLink 2.5.0 nightly download starting on March 24, 2013 from:

Java Model 

Below is the Java model that we will use for this example.  The model represents customer data.  We will use an object graph to output just enough information so that someone could contact the customer by phone.  In this example we won't specify the object graphs as part of the mapping metadata.

Customer 

package blog.objectgraphs.runtime;

import java.util.List;
import javax.xml.bind.annotation.*;

@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class Customer {

    @XmlAttribute
    private int id;

    private String name;

    private Address billingAddress;

    private Address shippingAddress;

    @XmlElementWrapper
    @XmlElement(name="phoneNumber")
    private List<PhoneNumber> phoneNumbers;

}

Address 

package blog.objectgraphs.runtime;

import javax.xml.bind.annotation.*;

@XmlAccessorType(XmlAccessType.FIELD)
public class Address {

    private String street;
    
    private String city;
    
    private String province;
    
    private String postalCode;

}

PhoneNumber 

package blog.objectgraphs.runtime;

import javax.xml.bind.annotation.*;

@XmlAccessorType(XmlAccessType.FIELD)
public class PhoneNumber {

    @XmlAttribute
    private String type;
    
    @XmlValue
    private String value;

}

Demo Code

Demo

In the demo code below we will read in an XML document to fully populate our Java model.  After marshalling it out to prove that everything was fully mapped we will create an object graph (lines 25-30).  For properties that correspond to domain objects we can create subgraphs (lines 27 & 29) to filter their values.  To be applied an object graph must be specified on the marshaler (line 33).  Finally we output the subset to both XML and JSON.

package blog.objectgraphs.runtime;

import java.io.File;
import javax.xml.bind.*;
import org.eclipse.persistence.jaxb.JAXBHelper;
import org.eclipse.persistence.jaxb.MarshallerProperties;
import org.eclipse.persistence.jaxb.ObjectGraph;
import org.eclipse.persistence.jaxb.Subgraph;

public class Demo {

    public static void main(String[] args) throws Exception {
        JAXBContext jc = JAXBContext.newInstance(Customer.class);

        Unmarshaller unmarshaller = jc.createUnmarshaller();
        File xml = new File("src/blog/objectgraphs/runtime/input.xml");
        Customer customer = (Customer) unmarshaller.unmarshal(xml);

        // Output XML
        Marshaller marshaller = jc.createMarshaller();
        marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
        marshaller.marshal(customer, System.out);

        // Create the Object Graph
        ObjectGraph contactInfo = JAXBHelper.getJAXBContext(jc).createObjectGraph(Customer.class);
        contactInfo.addAttributeNodes("name");
        Subgraph location = contactInfo.addSubgraph("billingAddress");
        location.addAttributeNodes("city", "province");
        Subgraph simple = contactInfo.addSubgraph("phoneNumbers");
        simple.addAttributeNodes("value");
        
        // Output XML - Based on Object Graph
        marshaller.setProperty(MarshallerProperties.OBJECT_GRAPH, contactInfo);
        marshaller.marshal(customer, System.out);

        // Output JSON - Based on Object Graph
        marshaller.setProperty(MarshallerProperties.MEDIA_TYPE, "application/json");
        marshaller.setProperty(MarshallerProperties.JSON_INCLUDE_ROOT, false);
        marshaller.setProperty(MarshallerProperties.JSON_WRAPPER_AS_ARRAY_NAME, true);
        marshaller.marshal(customer, System.out);
    }

}

input.xml/Output 

We will use the following document to populate our domain model.  We will also marshal it back out to demonstrate that all of the content is actually mapped.

<?xml version="1.0" encoding="UTF-8"?>
<customer id="123">
   <name>Jane Doe</name>
   <billingAddress>
      <street>1 A Street</street>
      <city>Any Town</city>
      <province>Ontario</province>
      <postalCode>A1B 2C3</postalCode>
   </billingAddress>
   <shippingAddress>
      <street>2 B Road</street>
      <city>Another Place</city>
      <province>Quebec</province>
      <postalCode>X7Y 8Z9</postalCode>
   </shippingAddress>
   <phoneNumbers>
      <phoneNumber type="work">555-1111</phoneNumber>
      <phoneNumber type="home">555-2222</phoneNumber>
   </phoneNumbers>
</customer>

XML Output Based on Object Graph

The XML below was produced by the exact same model as the previous XML document.  The difference is that we leveraged an object graph to select a subset of the mapped content.

<?xml version="1.0" encoding="UTF-8"?>
<customer>
   <name>Jane Doe</name>
   <billingAddress>
      <city>Any Town</city>
      <province>Ontario</province>
   </billingAddress>
   <phoneNumbers>
      <phoneNumber>555-1111</phoneNumber>
      <phoneNumber>555-2222</phoneNumber>
   </phoneNumbers>
</customer>

JSON Output Based on Object Graph

Below is the same subset as the previous XML document represented as JSON.  We have used the new JSON_WRAPPER_AS_ARRAY_NAME property (see Binding to JSON & XML - Handling Collections) to improve the representation of collection values.

{
   "name" : "Jane Doe",
   "billingAddress" : {
      "city" : "Any Town",
      "province" : "Ontario"
   },
   "phoneNumbers" : [ "555-1111", "555-2222" ]
}

Further Reading

If you enjoyed this post then you may also be interested in:

No comments:

Post a Comment

Note: Only a member of this blog may post a comment.