Jump to: navigation, search

EclipseLink/Examples/MOXy/ObjectGraphs/Inheritance

In previous posts we have explored how object graphs can be defined through metadata and programatically. In this post I'll demonstrate the impact of inheritance in your domain model on how you define object graphs.


Java Model

The following domain model will be used for this example.

Customer

package example.moxy.objectgraphs.inheritance;
 
import java.util.*;
import javax.xml.bind.annotation.*;
import org.eclipse.persistence.oxm.annotations.XmlNamedAttributeNode;
import org.eclipse.persistence.oxm.annotations.XmlNamedObjectGraph;
 
@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
@XmlNamedObjectGraph(
    name = "simple",
    attributeNodes = { 
        @XmlNamedAttributeNode(value="contactInfo", subgraph="simple") 
    }
)
public class Customer {
 
    private List<ContactInfo> contactInfo = new ArrayList<ContactInfo>();
 
}


ContactInfo

The @XmlNamedObjectGraph annotation is used to define the object graph for the root class, and @XmlNamedSubGraph is used to define the object graphs for the subclasses. We have indicated that we do not want to include any of the properties from the super class.

package example.moxy.objectgraphs.inheritance;
 
import javax.xml.bind.annotation.*;
import org.eclipse.persistence.oxm.annotations.*;
 
@XmlSeeAlso({Address.class, PhoneNumber.class})
@XmlAccessorType(XmlAccessType.FIELD)
@XmlNamedObjectGraph(
        name = "simple",
        attributeNodes = { 
        },
        subclassSubgraphs = {
            @XmlNamedSubgraph(
                name = "simple",
                type = Address.class,
                attributeNodes = { 
                    @XmlNamedAttributeNode("city")
                }
            ),
            @XmlNamedSubgraph(
                name = "simple",
                type = PhoneNumber.class,
                attributeNodes = {
                @XmlNamedAttributeNode("number")
            }
        )
    }
)
public abstract class ContactInfo {
 
    private int id;
 
}


Address

Address is a subclass of ContactInfo.

package example.moxy.objectgraphs.inheritance;
 
public class Address extends ContactInfo {
 
    private String street;
 
    private String city;
 
}


PhoneNumber

PhoneNumber is also a subclass of ContactInfo.

package example.moxy.objectgraphs.inheritance;
 
public class PhoneNumber extends ContactInfo {
 
    private String number;
 
    private String extension;
 
}

Demo Code

Below we will explore two different approaches for using object graphs.


Demo - Object Graph Specified through Metadata

In the demo code below we will leverage the object graphs that we defined via annotations to output a subset of the data to XML and JSON.

package blog.objectgraphs.inheritance;
 
import java.io.File;
import javax.xml.bind.*;
import org.eclipse.persistence.jaxb.MarshallerProperties;
 
public class DemoMetadata {
 
    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/inheritance/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);
 
        // Output XML - Based on Object Graph
        marshaller.setProperty(MarshallerProperties.OBJECT_GRAPH, "simple");
        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.marshal(customer, System.out);
    }
 
}


Demo - Object Graph Created Programatically

In the demo code below we will ignore the object graphs defined via metadata and create an equivalent object graph programatically. One instance of ObjectGraph is created, and for each mapped property that we want a subset of we will create an instance of Subgraph. When the properties type has subclasses we can create an instance of Subgraph for each class in the inheritance hierarchy.

package blog.objectgraphs.inheritance;
 
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 DemoRuntime {
 
    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/inheritance/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 simple = JAXBHelper.getJAXBContext(jc).createObjectGraph(Customer.class);
        Subgraph contactInfoSG = simple.addSubgraph("contactInfo", ContactInfo.class);
        Subgraph addressSG = simple.addSubgraph("contactInfo", Address.class);
        addressSG.addAttributeNodes("city");
        Subgraph phoneNumberSG = simple.addSubgraph("contactInfo", PhoneNumber.class);
        phoneNumberSG.addAttributeNodes("number");
 
        // Output XML - Based on Object Graph
        marshaller.setProperty(MarshallerProperties.OBJECT_GRAPH, simple);
        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.marshal(customer, System.out);
    }
 
}


Input/Output

The following input and output are the same for both the metadata driven and programmatic demos.


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>
    <contactInfo xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="address">
        <id>1</id>
        <street>1 A Street</street>
        <city>Any Town</city>
    </contactInfo>
    <contactInfo xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="phoneNumber">
        <id>2</id>
        <number>555-1111</number>
        <extension>123</extension>
    </contactInfo>
</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 a named object graph to select a subset of the mapped content.

<?xml version="1.0" encoding="UTF-8"?>
<customer>
   <contactInfo xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="address">
      <city>Any Town</city>
   </contactInfo>
   <contactInfo xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="phoneNumber">
      <number>555-1111</number>
   </contactInfo>
</customer>


JSON Output Based on Object Graph

Below is the same subset as the previous XML document represented as JSON.

{
   "contactInfo" : [ {
      "type" : "address",
      "city" : "Any Town"
   }, {
      "type" : "phoneNumber",
      "number" : "555-1111"
   } ]
}