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

EclipseLink/Development/396542

< EclipseLink‎ | Development
Revision as of 16:50, 13 December 2012 by Matt.macivor.oracle.com (Talk | contribs) (Configuration:)

Design Specification: XmlObjectGraphs (PropertyGroups)

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.

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:

@Target({TYPE})
@Retention(RUNTIME)
public @interface XmlObjectGraphs{
     XmlObjectGraph[] value();
}
@Target({TYPE})
@Retention(RUNTIME)
public @interface XmlObjectGraph {
    /**
     * 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.
     */
    IncludedProperty[] properties();
 
    /**
     * Optional: a list of named subgraphs that are referenced 
     * from the property entries.
     */
    XmlSubGraph[] subGraphs();
 
    /**
     * Optional: a list of named subgraphs for any subclasses
     * of this class.
     */
    XmlSubGraph[] subclassGraphs();
}
@Target({TYPE})
@Retention(RUNTIME)
public @interface IncludedProperty
    /**
     * 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 XmlSubGraph {
   /**
    * 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
    */
   IncludedProperty[]  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:

@XmlObjectGraph(name=”simple”, 
   properties={@IncludedProperty(value=”firstName”), 
               @IncludedProperty(value=”address”, subgraph=”simple”)
   }
)
public class Employee {
   public String firstName;
   public String lastName;
   public Address address;
}
@XmlObjectGraph(name="simple", 
   properties={
      @IncludedProperty(value="firstName"), 
      @IncludedProperty(value="address", subgraph="basic")
   }, 
   subgraphs={
      @XmlSubGraph(name="basic", 
         properties={
            @IncludedProperty("city")
          }
   }
)
public class Customer {
   public String firstName;
   public String lastName;
   public Address address;
}
 
@XmlObjectGraphs(name="simple", 
   properties = {
      @IncludedProperty("street"), 
      @IncludedProperty("city"), 
      @IncludedProperty("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.

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:

@XmlObjectGraph(name=”simple”, properties={@IncludedProperty(“province”)})
public class CanadianAddress extends Address {
  public String province;
}
@XmlObjectGraph(name=”simple”, properties={@IncludedProperty(“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.JAXB_OBJECT_GRAPH, “simple”);
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.

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