Skip to main content

Notice: this Wiki will be going read only early in 2024 and edits will no longer be possible. Please see: https://gitlab.eclipse.org/eclipsefdn/helpdesk/-/wikis/Wiki-shutdown-plan for the plan.

Jump to: navigation, search

Difference between revisions of "EclipseLink/Development/396542"

(Runtime API:)
(Programmatic)
 
(9 intermediate revisions by the same user not shown)
Line 10: Line 10:
 
#Allow different operations to use different subsets of attributes.
 
#Allow different operations to use different subsets of attributes.
 
#Minimal impact on performance for the default case.
 
#Minimal impact on performance for the default case.
 +
#Align with the JPA NamedEntityGraph API.
  
 
= Configuration: =
 
= Configuration: =
The configuration for this feature will use a set of annotations to set up attribute groups on the descriptor. The following annotations will be defined:
+
The configuration for this feature will use a set of annotations to set up attribute groups on the descriptor. The following annotations will be defined. The annotations make use of the Xml prefix to differentiate them from the JPA equivalent annotations:
 
<div style="width:700px">
 
<div style="width:700px">
 
<source lang="java">
 
<source lang="java">
 
@Target({TYPE})
 
@Target({TYPE})
 
@Retention(RUNTIME)
 
@Retention(RUNTIME)
public @interface NamedObjectGraphs{
+
public @interface XmlNamedObjectGraphs{
     NamedObjectGraph[] value();
+
     XmlNamedObjectGraph[] value();
 
}  
 
}  
 
</source>
 
</source>
Line 27: Line 28:
 
@Target({TYPE})
 
@Target({TYPE})
 
@Retention(RUNTIME)
 
@Retention(RUNTIME)
public @interface NamedObjectGraph {
+
public @interface XmlNamedObjectGraph {
 
     /**
 
     /**
 
     * The name of this object graph. Defaults to the name of the class
 
     * The name of this object graph. Defaults to the name of the class
Line 36: Line 37:
 
     * The list of properties to be marshalled/unmarshalled for this graph.
 
     * The list of properties to be marshalled/unmarshalled for this graph.
 
     */
 
     */
     IncludedProperty[] properties();
+
     XmlNamedAttributeNode[] attributeNodes();
  
 
     /**
 
     /**
Line 42: Line 43:
 
     * from the property entries.
 
     * from the property entries.
 
     */
 
     */
     NamedSubGraph[] subGraphs();
+
     XmlNamedSubGraph[] subGraphs();
  
 
     /**
 
     /**
Line 48: Line 49:
 
     * of this class.
 
     * of this class.
 
     */
 
     */
     NamedSubGraph[] subclassGraphs();
+
     XmlNamedSubGraph[] subclassGraphs();
 
}     
 
}     
 
</source>
 
</source>
Line 57: Line 58:
 
@Target({TYPE})
 
@Target({TYPE})
 
@Retention(RUNTIME)
 
@Retention(RUNTIME)
public @interface IncludedProperty
+
public @interface NamedAttributeNode
 
     /**
 
     /**
 
     * required: the name of the property
 
     * required: the name of the property
Line 75: Line 76:
 
@Target({TYPE})
 
@Target({TYPE})
 
@Retention(RUNTIME)
 
@Retention(RUNTIME)
public @interface NamedSubGraph {
+
public @interface XmlNamedSubGraph {
 
   /**
 
   /**
 
     * required: the name of the subgraph
 
     * required: the name of the subgraph
Line 90: Line 91:
 
     * The list of properties to include in this graph
 
     * The list of properties to include in this graph
 
     */
 
     */
   IncludedProperty[]  properties();
+
   XmlNamedAttributeNode[]  properties();
 
}
 
}
 
</source>
 
</source>
Line 100: Line 101:
 
<div style="width:700px">
 
<div style="width:700px">
 
<source lang="java">
 
<source lang="java">
@NamedObjectGraph(name=”simple”,  
+
@XmlNamedObjectGraph(name="simple",  
   properties={@IncludedProperty(value=”firstName”),  
+
   properties={@XmlNamedAttributeNode(value="firstName"),  
               @IncludedProperty(value=”address”, subgraph=”simple”)
+
               @XmlNamedAttributeNode(value="address", subgraph="simple")
 
   }
 
   }
 
)
 
)
Line 110: Line 111:
 
   public Address address;
 
   public Address address;
 
}
 
}
@NamedObjectGraph(name="simple",  
+
@XmlNamedObjectGraph(name="simple",  
 
   properties={
 
   properties={
       @IncludedProperty(value="firstName"),  
+
       @XmlNamedAttributeNode(value="firstName"),  
       @IncludedProperty(value="address", subgraph="basic")
+
       @XmlNamedAttributeNode(value="address", subgraph="basic")
 
   },  
 
   },  
 
   subgraphs={
 
   subgraphs={
       @NamedSubGraph(name="basic",  
+
       @XmlNamedSubGraph(name="basic",  
 
         properties={
 
         properties={
             @IncludedProperty("city")
+
             @XmlNamedAttributeNode("city")
 
           }
 
           }
 
   }
 
   }
Line 128: Line 129:
 
}
 
}
  
@NamedObjectGraphs(name="simple",  
+
@XmlNamedObjectGraphs(name="simple",  
 
   properties = {
 
   properties = {
       @IncludedProperty("street"),  
+
       @XmlNamedAttributeNode("street"),  
       @IncludedProperty("city"),  
+
       @XmlNamedAttributeNode("city"),  
       @IncludedProperty("country")
+
       @XmlNamedAttributeNode("country")
 
     }
 
     }
 
)
 
)
Line 146: Line 147:
  
 
'''External Metadata'''
 
'''External Metadata'''
The equivalent of these annotations will also be provided in the eclispelink oxm external meta-data.  
+
The equivalent of these annotations will also be provided in the eclispelink oxm external meta-data. The schema for these annotations will be as follows:
 +
<div style="width:1024px">
 +
<source lang="xml">
 +
    <xs:element name="xml-named-object-graphs">
 +
        <xs:complexType>
 +
            <xs:sequence>
 +
                <xs:element ref="xml-named-object-graph" maxOccurs="unbounded"/>
 +
            </xs:sequence>
 +
        </xs:complexType>
 +
    </xs:element>
  
'''Programmatic'''
+
    <xs:element name="xml-named-object-graph">
The underlying implementation of this feature will make use of AttributeGroups on the XML Descriptors. In the case that Annotations and External Metadata are insufficent, it would also be possible to configure the ObjectGraphs in a cusomizer by dealing directly with the XMLDescriptors and the existing AttributeGroup API.  
+
        <xs:complexType>
 +
            <xs:sequence>
 +
                <xs:element name="xml-named-attribute-node" type="xml-named-attribute-node" minOccurs="0" maxOccurs="unbounded"/>
 +
                <xs:element name="xml-named-subgraph" type="xml-named-subgraph" minOccurs="0" maxOccurs="unbounded"/>
 +
                <xs:element name="xml-named-subclass-graph" type="xml-named-subgraph" minOccurs="0" maxOccurs="unbounded"/>
 +
            </xs:sequence>
 +
            <xs:attribute name="name" type="xs:string"/>
 +
        </xs:complexType>
 +
    </xs:element>
 +
 
 +
    <xs:complexType name="xml-named-attribute-node">
 +
        <xs:attribute name="name" type="xs:string"/>
 +
        <xs:attribute name="subgraph" type="xs:string"/>
 +
    </xs:complexType>
 +
 
 +
    <xs:complexType name="xml-named-subgraph">
 +
        <xs:sequence>
 +
            <xs:element name="xml-named-attribute-node" type="xml-named-attribute-node" minOccurs="0" maxOccurs="unbounded"/>
 +
        </xs:sequence>
 +
        <xs:attribute name="name" type="xs:string"/>
 +
        <xs:attribute name="type" type="xs:string"/>
 +
    </xs:complexType>
 +
</source>
 +
</div>
 +
 
 +
= Programmatic =
 +
A set of interfaces will be defined to allow for the creation of ObjectGraphs dynamically at runtime. These ObjectGraphs can be set on the Marshaller or Unmarshaller instead of the name of a pre-defined ObjectGraph.
 +
 
 +
The following interfaces will be provided:
 +
<div style="width:700px">
 +
<source lang="java">
 +
public interface ObjectGraph {
 +
    /**
 +
    * Returns the name of the static EntityGraph.  Will return null if the
 +
    * EntityGraph is not a named EntityGraph.
 +
    */
 +
    public String getName();
 +
 
 +
    /*
 +
    * Add an AttributeNode attribute to the entity graph.
 +
    *
 +
    * @throws IllegalArgumentException if the attribute is not an attribute of
 +
    * this entity.
 +
    * @throws IllegalStateException if this EntityGraph has been statically
 +
    * defined
 +
    */
 +
    public void addAttributeNodes(String ... attributeName);
 +
 
 +
 
 +
    /*
 +
    * Used to add a node of the graph that corresponds to a managed type. This
 +
    * allows for construction of multi-node Entity graphs that include related
 +
    * managed types.
 +
    *
 +
    * @throws IllegalArgumentException if the attribute is not an attribute of
 +
    * this entity.
 +
    * @throws IllegalArgumentException if the attribute's target type is not a
 +
    * managed type
 +
    *
 +
    * @throws IllegalStateException if this EntityGraph has been statically
 +
    * defined
 +
    */
 +
    public Subgraph addSubgraph(String attribute);
 +
 
 +
    /**
 +
    * Used to add a node of the graph that corresponds to a managed type with
 +
    * inheritance.  This allows for multiple subclass sub-graphs to be defined
 +
    * for this node of the entity graph.  Subclass sub-graphs will include the
 +
    * specified attributes of superclass sub-graphs
 +
    *
 +
    * @throws IllegalArgumentException if the attribute is not an attribute of
 +
    * this managed type.
 +
    * @throws IllegalArgumentException
 +
    *            if the attribute's target type is not a managed type
 +
    * @throws IllegalStateException
 +
    *            if this EntityGraph has been statically defined
 +
    */
 +
    public Subgraph addSubgraph(String attribute, Class type);
 +
 
 +
    /*
 +
    * returns the attributes of this entity that are included in the entity
 +
    * graph
 +
    */
 +
    public List<AttributeNode> getAttributeNodes();
 +
 
 +
}
 +
 
 +
</source>
 +
</div>
 +
 
 +
<div style="width:700px">
 +
<source lang="java">
 +
public interface Subgraph extends AttributeNode {
 +
 
 +
    /**
 +
    * Add an AttributeNode attribute to the entity graph.
 +
    *
 +
    * @throws IllegalArgumentException if the attribute is not an attribute of
 +
    * this managed type.
 +
    * @throws IllegalStateException
 +
    *            if this EntityGraph has been statically defined
 +
    */
 +
    public void  addAttributeNodes(String ... attributeName);
 +
 
 +
    /**
 +
    * Used to add a node of the graph that corresponds to a managed type. This
 +
    * allows for construction of multi-node Entity graphs that include related
 +
    * managed types.
 +
    *
 +
    * @throws IllegalArgumentException if the attribute is not an attribute of
 +
    * this managed type.
 +
    * @throws IllegalArgumentException
 +
    *            if the attribute's target type is not a managed type
 +
    * @throws IllegalStateException
 +
    *            if this EntityGraph has been statically defined
 +
    */
 +
    public Subgraph addSubgraph(String attribute);
 +
 
 +
    /**
 +
    * Used to add a node of the graph that corresponds to a managed type with
 +
    * inheritance.  This allows for multiple subclass sub-graphs to be defined
 +
    * for this node of the entity graph. Subclass sub-graphs will include the
 +
    * specified attributes of superclass sub-graphs
 +
    *
 +
    * @throws IllegalArgumentException if the attribute is not an attribute of
 +
    * this managed type.
 +
    * @throws IllegalArgumentException
 +
    *            if the attribute's target type is not a managed type
 +
    * @throws IllegalStateException
 +
    *            if this EntityGraph has been statically defined
 +
    */
 +
    public Subgraph addSubgraph(String attribute, Class type);
 +
 
 +
 
 +
 
 +
    /**
 +
    * returns the attributes of this managed type that are included in the
 +
    * sub-graph
 +
    */
 +
    public List<AttributeNode> getAttributeNodes();
 +
 
 +
    /**
 +
    * returns the attribute that references this sub-graph.
 +
    /
 +
    public <T> Attribute<T,X> getReferencingAttribute();
 +
 
 +
    /**
 +
    * returns the type of this sub-graph if it was used to extend a superclass
 +
    * sub-graph definition.
 +
    */
 +
    public Class getClassType();
 +
}
 +
</source>
 +
</div>
 +
 
 +
 
 +
<div style="width:700px">
 +
<source lang="java">
 +
/**
 +
* Represents an AttributeNode of an entity graph.
 +
*/
 +
public interface AttributeNode {
 +
 
 +
    /*
 +
    * returns the name of the referencing attribute.
 +
    */
 +
    public String getAttributeName();
 +
}
 +
</source>
 +
</div>
 +
 
 +
API will be added to JAXBContext to create a new instance of Object Graph.  
 +
 
 +
Example:
 +
<div style="width:700px">
 +
<source lang="java">
 +
ObjectGraph graph = JAXBContext.createObjectGraph(Customer.class);
 +
graph.addAttributeNodes("lastName");
 +
graph.addAttributeNodes("age");
 +
SubGraph<Address> subGraph = graph.addSubGraph("address", Address.class);
 +
subGraph.addAttributeNodes("country");
 +
 
 +
jaxbMarshaller.setProperty(MarshallerProperties.MARSHAL_OBJECT_GRAPH, graph);
 +
</source>
 +
</div>
  
 
= Inheritance: =
 
= Inheritance: =
Line 156: Line 350:
 
<div style="width:700px">
 
<div style="width:700px">
 
<source lang="java">
 
<source lang="java">
@NamedObjectGraph(name="simple", properties={@IncludedProperty("province")})
+
@XmlNamedObjectGraph(name="simple", properties={@XmlNamedAttributeNode("province")})
 
public class CanadianAddress extends Address {
 
public class CanadianAddress extends Address {
 
   public String province;
 
   public String province;
 
}
 
}
@NamedObjectGraph(name="simple", properties={@IncludedProperty("state")})
+
@XmlNamedObjectGraph(name="simple", properties={@XmlNamedAttributeNode("state")})
 
public class AmericanAddress extends Address {
 
public class AmericanAddress extends Address {
 
   public String state;
 
   public String state;
Line 167: Line 361:
 
</div>
 
</div>
  
Now when marshalling using the “simple” fetch group for address, if the Address is a CanadianAddress then street, city, country and also province will be included.
+
Now when marshalling using the "simple" fetch group for address, if the Address is a CanadianAddress then street, city, country and also province will be included.
  
 
= ChoiceMappings: =
 
= ChoiceMappings: =
Line 179: Line 373:
 
JAXBContext ctx = JAXBContext.newInstance("foo.bar");
 
JAXBContext ctx = JAXBContext.newInstance("foo.bar");
 
Marshaller m = ctx.createMarshaller();
 
Marshaller m = ctx.createMarshaller();
m.setProperty(MarshalProperties.MARSHAL_OBJECT_GRAPH, "simple");
+
m.setProperty(MarshalProperties.OBJECT_GRAPH, "simple");
 
m.marshal(myCustomer, System.out);
 
m.marshal(myCustomer, System.out);
 
</source>
 
</source>
 
</div>
 
</div>
 +
 +
Additionally the OBJECT_GRAPH can be specified on the context, and will then apply to any Marshallers or Unmarshallers created from that context after the property is set.
 +
 +
<div style="width:700px">
 +
<source lang="java">
 +
JAXBContext ctx = JAXBContext.newInstance("foo.bar");
 +
ctx.setProperty(MarshalProperties.OBJECT_GRAPH, "simple");
 +
Marshaller m = ctx.createMarshaller();
 +
m.marshal(myCustomer, System.out);
 +
</source>
 +
</div>
 +
  
 
= Error Cases: =
 
= Error Cases: =
 
If an AttributeNode is specified that doesn't correspond to an actual attribute on the domain class, an exception will be thrown
 
If an AttributeNode is specified that doesn't correspond to an actual attribute on the domain class, an exception will be thrown
If a subgraph is specified then a subgraph with that name must exist or this is an exception case. In the case of a choice mapping, then at least one of the potential targets of that mapping must have a subGraph defined with that name.  
+
 
 +
If a subgraph is specified then a subgraph with that name must exist or this is an exception case. In the case of a choice mapping, then at least one of the potential targets of that mapping must have a subGraph defined with that name.
 +
 
 +
If the OBJECT_GRAPH property is set to a name on the Marshaller or Unmarshaller, and there is no such ObjectGraph defined for the Object being marshalled/unmarshalled then an exception will be thrown.
  
 
= Open Issues =  
 
= Open Issues =  
  
 
#The underlying implementation in the runtime will make use of the existing AttributeGroup/AttributeItem classes. The dependency on these classes being added into the MOXy runtime will need to be considered as part of the ongoing footprint reduction work in MOXy.
 
#The underlying implementation in the runtime will make use of the existing AttributeGroup/AttributeItem classes. The dependency on these classes being added into the MOXy runtime will need to be considered as part of the ongoing footprint reduction work in MOXy.

Latest revision as of 12:49, 3 April 2013

Design Specification: MOXy NamedObjectGraphs

ER 396542

Currently EclipseLink provides support for FetchGroups/AttributeGroups in ORM to control which attributes of a given object are to be written out and read in. Similar configuration should be allowed in OXM/JAXB to allow a subset of attributes to be marshaled and unmarshaled.

Requirements:

  1. Provide an API to configure which attributes should be marshaled and unmarshaled.
  2. Allow for nested subgraphs to be defined inline or globally on the target class.
  3. Allow different operations to use different subsets of attributes.
  4. Minimal impact on performance for the default case.
  5. Align with the JPA NamedEntityGraph API.

Configuration:

The configuration for this feature will use a set of annotations to set up attribute groups on the descriptor. The following annotations will be defined. The annotations make use of the Xml prefix to differentiate them from the JPA equivalent annotations:

@Target({TYPE})
@Retention(RUNTIME)
public @interface XmlNamedObjectGraphs{
     XmlNamedObjectGraph[] value();
}
@Target({TYPE})
@Retention(RUNTIME)
public @interface XmlNamedObjectGraph {
    /**
     * The name of this object graph. Defaults to the name of the class
     */
    String name();
 
    /**
     * The list of properties to be marshalled/unmarshalled for this graph.
     */
    XmlNamedAttributeNode[] attributeNodes();
 
    /**
     * Optional: a list of named subgraphs that are referenced 
     * from the property entries.
     */
    XmlNamedSubGraph[] subGraphs();
 
    /**
     * Optional: a list of named subgraphs for any subclasses
     * of this class.
     */
    XmlNamedSubGraph[] subclassGraphs();
}
@Target({TYPE})
@Retention(RUNTIME)
public @interface NamedAttributeNode
    /**
     * required: the name of the property
     */
    String value();
 
    /** optional: if this property referenced another JAXB Object, 
     *  specify the name of the object graph to use for that nested object. 
     */ By default, the full object will be read.
    String subgraph();
}
@Target({TYPE})
@Retention(RUNTIME)
public @interface XmlNamedSubGraph {
   /**
    * required: the name of the subgraph
    */
   String name();
 
   /**
    * optional: only required for inheritance or with ChoiceMappings 
    * to specify which of the possible targets this subgraph is to be
    * applied to. 
   Class type();
 
   /**
    * The list of properties to include in this graph
    */
   XmlNamedAttributeNode[]  properties();
}

This allows for the user to specify a list of ObjectGraphs for each JAXB object, and to reference those from within each other. In the case that a targetObjectGraphName exists as both a subgraph of the current ObjectGraph AND as defined on the target class, the subgraph will win.

Example:

@XmlNamedObjectGraph(name="simple", 
   properties={@XmlNamedAttributeNode(value="firstName"), 
               @XmlNamedAttributeNode(value="address", subgraph="simple")
   }
)
public class Employee {
   public String firstName;
   public String lastName;
   public Address address;
}
@XmlNamedObjectGraph(name="simple", 
   properties={
      @XmlNamedAttributeNode(value="firstName"), 
      @XmlNamedAttributeNode(value="address", subgraph="basic")
   }, 
   subgraphs={
      @XmlNamedSubGraph(name="basic", 
         properties={
            @XmlNamedAttributeNode("city")
          }
   }
)
public class Customer {
   public String firstName;
   public String lastName;
   public Address address;
}
 
@XmlNamedObjectGraphs(name="simple", 
   properties = {
      @XmlNamedAttributeNode("street"), 
      @XmlNamedAttributeNode("city"), 
      @XmlNamedAttributeNode("country")
    }
)
public class Address {
   public String street;
   public String city;
   public String country;
   public String zipCode;
}

In this example, both Customer and Employee have “simple” fetch groups which only include the first name and the address. However, Customer is interested in only the city attribute on the address in it's simple form, whereas Employee wants a little more information, so uses the fetch group on Address that includes street, city, and country. Customer defines it's nested fetch group for Address inline. At marshal or unmarshal time, the name of the fetch group on the root object being marshalled (or unmarshalled) would be specified as part of the operation, and the appropriate attributes would be included.

External Metadata The equivalent of these annotations will also be provided in the eclispelink oxm external meta-data. The schema for these annotations will be as follows:

    <xs:element name="xml-named-object-graphs">
        <xs:complexType>
            <xs:sequence>
                <xs:element ref="xml-named-object-graph" maxOccurs="unbounded"/>
            </xs:sequence>
        </xs:complexType>
    </xs:element>
 
    <xs:element name="xml-named-object-graph">
        <xs:complexType>
            <xs:sequence>
                <xs:element name="xml-named-attribute-node" type="xml-named-attribute-node" minOccurs="0" maxOccurs="unbounded"/>
                <xs:element name="xml-named-subgraph" type="xml-named-subgraph" minOccurs="0" maxOccurs="unbounded"/>
                <xs:element name="xml-named-subclass-graph" type="xml-named-subgraph" minOccurs="0" maxOccurs="unbounded"/>
            </xs:sequence>
            <xs:attribute name="name" type="xs:string"/>
        </xs:complexType>
    </xs:element>
 
    <xs:complexType name="xml-named-attribute-node">
        <xs:attribute name="name" type="xs:string"/>
        <xs:attribute name="subgraph" type="xs:string"/>
    </xs:complexType>
 
    <xs:complexType name="xml-named-subgraph">
        <xs:sequence>
            <xs:element name="xml-named-attribute-node" type="xml-named-attribute-node" minOccurs="0" maxOccurs="unbounded"/>
        </xs:sequence>
        <xs:attribute name="name" type="xs:string"/>
        <xs:attribute name="type" type="xs:string"/>
    </xs:complexType>

Programmatic

A set of interfaces will be defined to allow for the creation of ObjectGraphs dynamically at runtime. These ObjectGraphs can be set on the Marshaller or Unmarshaller instead of the name of a pre-defined ObjectGraph.

The following interfaces will be provided:

public interface ObjectGraph {
    /**
     * Returns the name of the static EntityGraph.  Will return null if the
     * EntityGraph is not a named EntityGraph.
     */
    public String getName();
 
    /*
     * Add an AttributeNode attribute to the entity graph.
     *
     * @throws IllegalArgumentException if the attribute is not an attribute of
     * this entity.
     * @throws IllegalStateException if this EntityGraph has been statically
     * defined
     */
    public void addAttributeNodes(String ... attributeName);
 
 
    /*
     * Used to add a node of the graph that corresponds to a managed type. This
     * allows for construction of multi-node Entity graphs that include related
     * managed types.
     *
     * @throws IllegalArgumentException if the attribute is not an attribute of
     * this entity.
     * @throws IllegalArgumentException if the attribute's target type is not a
     * managed type
     *
     * @throws IllegalStateException if this EntityGraph has been statically
     * defined
     */
    public Subgraph addSubgraph(String attribute);
 
    /**
     * Used to add a node of the graph that corresponds to a managed type with
     * inheritance.  This allows for multiple subclass sub-graphs to be defined
     * for this node of the entity graph.  Subclass sub-graphs will include the
     * specified attributes of superclass sub-graphs
     *
     * @throws IllegalArgumentException if the attribute is not an attribute of
     * this managed type.
     * @throws IllegalArgumentException
     *             if the attribute's target type is not a managed type
     * @throws IllegalStateException
     *             if this EntityGraph has been statically defined
     */
    public Subgraph addSubgraph(String attribute, Class type);
 
    /*
     * returns the attributes of this entity that are included in the entity
     * graph
     */
    public List<AttributeNode> getAttributeNodes();
 
}
public interface Subgraph extends AttributeNode {
 
    /**
     * Add an AttributeNode attribute to the entity graph.
     *
     * @throws IllegalArgumentException if the attribute is not an attribute of
     * this managed type.
     * @throws IllegalStateException
     *             if this EntityGraph has been statically defined
     */
    public void  addAttributeNodes(String ... attributeName);
 
    /**
     * Used to add a node of the graph that corresponds to a managed type. This
     * allows for construction of multi-node Entity graphs that include related
     * managed types.
     *
     * @throws IllegalArgumentException if the attribute is not an attribute of
     * this managed type.
     * @throws IllegalArgumentException
     *             if the attribute's target type is not a managed type
     * @throws IllegalStateException
     *             if this EntityGraph has been statically defined
     */
    public Subgraph addSubgraph(String attribute);
 
    /**
     * Used to add a node of the graph that corresponds to a managed type with
     * inheritance.  This allows for multiple subclass sub-graphs to be defined
     * for this node of the entity graph. Subclass sub-graphs will include the
     * specified attributes of superclass sub-graphs
     *
     * @throws IllegalArgumentException if the attribute is not an attribute of
     * this managed type.
     * @throws IllegalArgumentException
     *             if the attribute's target type is not a managed type
     * @throws IllegalStateException
     *             if this EntityGraph has been statically defined
     */
    public Subgraph addSubgraph(String attribute, Class type);
 
 
 
    /**
     * returns the attributes of this managed type that are included in the
     * sub-graph
     */
    public List<AttributeNode> getAttributeNodes();
 
    /**
     * returns the attribute that references this sub-graph.
     /
    public <T> Attribute<T,X> getReferencingAttribute();
 
    /**
     * returns the type of this sub-graph if it was used to extend a superclass
     * sub-graph definition.
     */
    public Class getClassType();
}


/**
 * Represents an AttributeNode of an entity graph.
 */
public interface AttributeNode {
 
    /*
     * returns the name of the referencing attribute.
     */
    public String getAttributeName();
}

API will be added to JAXBContext to create a new instance of Object Graph.

Example:

ObjectGraph graph = JAXBContext.createObjectGraph(Customer.class);
graph.addAttributeNodes("lastName");
graph.addAttributeNodes("age");
SubGraph<Address> subGraph = graph.addSubGraph("address", Address.class);
subGraph.addAttributeNodes("country");
 
jaxbMarshaller.setProperty(MarshallerProperties.MARSHAL_OBJECT_GRAPH, graph);

Inheritance:

For the purpose of inheritance, by specifying an ObjectGraph on the subclass with the same name as the one specified on the superclass, the fetch group will extend the one from it's parent. Additionally, for subgraphs that are defined inline, multiple graphs with the same name can be used by specifying the type. For example, if we add CanadianAddress and AmericanAddress to the above example:

@XmlNamedObjectGraph(name="simple", properties={@XmlNamedAttributeNode("province")})
public class CanadianAddress extends Address {
  public String province;
}
@XmlNamedObjectGraph(name="simple", properties={@XmlNamedAttributeNode("state")})
public class AmericanAddress extends Address {
  public String state;
}

Now when marshalling using the "simple" fetch group for address, if the Address is a CanadianAddress then street, city, country and also province will be included.

ChoiceMappings:

One thing that differentiates OXM and JAXB from ORM is the concept of choice mappings. In this case an attribute can map to one or more different, unrelated objects. This could be handled in the same way as inheritance, where a subgraph is specified with the same name for each of the potential targets of this attribute. If the actual target at runtime doesn't include a graph/group by that name, then the default group is used.

Runtime API:

At runtime, when marshalling or unmarshalling, a property can be specified on the Marshaller or Unmarshaller to state which ObjectGraph should be used during marshalling:

JAXBContext ctx = JAXBContext.newInstance("foo.bar");
Marshaller m = ctx.createMarshaller();
m.setProperty(MarshalProperties.OBJECT_GRAPH, "simple");
m.marshal(myCustomer, System.out);

Additionally the OBJECT_GRAPH can be specified on the context, and will then apply to any Marshallers or Unmarshallers created from that context after the property is set.

JAXBContext ctx = JAXBContext.newInstance("foo.bar");
ctx.setProperty(MarshalProperties.OBJECT_GRAPH, "simple");
Marshaller m = ctx.createMarshaller();
m.marshal(myCustomer, System.out);


Error Cases:

If an AttributeNode is specified that doesn't correspond to an actual attribute on the domain class, an exception will be thrown

If a subgraph is specified then a subgraph with that name must exist or this is an exception case. In the case of a choice mapping, then at least one of the potential targets of that mapping must have a subGraph defined with that name.

If the OBJECT_GRAPH property is set to a name on the Marshaller or Unmarshaller, and there is no such ObjectGraph defined for the Object being marshalled/unmarshalled then an exception will be thrown.

Open Issues

  1. The underlying implementation in the runtime will make use of the existing AttributeGroup/AttributeItem classes. The dependency on these classes being added into the MOXy runtime will need to be considered as part of the ongoing footprint reduction work in MOXy.

Back to the top