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/339381"

(Configuration)
(Undo revision 257129 by Rick.barkhouse.oracle.com (Talk))
 
(36 intermediate revisions by 2 users not shown)
Line 1: Line 1:
 
<div style="margin:5px;float:right;border:1px solid #000000;padding:5px">__TOC__</div>
 
<div style="margin:5px;float:right;border:1px solid #000000;padding:5px">__TOC__</div>
  
= Design Specification: XML Extensions =
+
= Design Documentation: XML Virtual Access Methods =
  
 
[http://bugs.eclipse.org/339381 ER 339381]
 
[http://bugs.eclipse.org/339381 ER 339381]
Line 7: Line 7:
 
Currently, EclipseLink MOXy supports the mapping of Java fields and properties to XML.  Said another way; in order to map data to XML, the user must have an existing Java field or property to map.
 
Currently, EclipseLink MOXy supports the mapping of Java fields and properties to XML.  Said another way; in order to map data to XML, the user must have an existing Java field or property to map.
  
To support multi-tenancy, we will be allowing the user to add additional mappings at runtime.  Because these new mappings would not have existing fields / properties on the Java class to map to, we will introduce the concept of "extensions", where we can instead rely on special <tt>get()</tt> and <tt>set()</tt> methods to maintain extension data.
+
To support multi-tenancy, we will be allowing the user to add additional mappings at runtime.  Because these new mappings would not have existing fields / properties on the Java class to map to, we will introduce the concept of virtual properties, where we can instead rely on special <tt>get()</tt> and <tt>set()</tt> methods to maintain extension data.
  
  
Line 13: Line 13:
  
 
# Users must be able to add new mappings at runtime through EclipseLink OXM
 
# Users must be able to add new mappings at runtime through EclipseLink OXM
# Users should be able to add any type of MOXy mapping as an extension
+
# Users should be able to add any type of MOXy mapping as virtual property
# Users must be able to annotate a field on their Java objects to hold extensions
+
# Users must be able to specify that a Java type virtual access methods, using either Annotations or EclipseLink OXM
## The Java field must be of type <tt>Map<String, Object></tt>
+
# Users must be able to specify a field in EclipseLink OXM to hold extensions
+
  
  
 
= Configuration =
 
= Configuration =
  
In order to add extensions to an entity:
+
In order to add virtual properties to an entity:
  
* the Java class must be marked with an <tt>@XmlExtensible</tt> annotation, or <tt><xml-extensible></tt> element in OXM
+
* the Java class must be marked with an <tt>@XmlVirtualAccessMethods</tt> annotation, or <tt><xml-virtual-access-methods></tt> element in OXM
* the Java class must contain getter and setter methods to access extension values
+
* the Java class must contain getter and setter methods to access virtual property values
** <tt>public Object get(String extensionName)</tt>
+
** <tt>public Object get(String propertyName)</tt>
** <tt>public void set(String extensionName, Object extensionValue)</tt>
+
** <tt>public void set(String propertyName, Object value)</tt>
 
** method names are configurable but must have the same method signatures as above
 
** method names are configurable but must have the same method signatures as above
* If the extension data is held in a field on the Java class, and if the Java class is using <tt>XmlAccessType.FIELD</tt>, then the extension field must be marked as <tt>@XmlTransient</tt>.
+
 
 +
 
 +
<div style="width:800px">
 +
{{tip||By default, EclipseLink will look for methods named "set" and "get". To customize accessor method names, see [[#Specifying Alternate Accessor Methods|Specifying Alternate Accessor Methods]].}}
 +
</div>
  
  
 
== Annotations ==
 
== Annotations ==
  
The user can specify that a Java class may hold extensions by using the <tt>@XmlExtensions</tt> annotation:
+
The user can specify that a Java class may hold virtual properties by using the <tt>@XmlVirtualAccessMethods</tt> annotation:
  
<div style="width:700px">
+
<div style="width:900px">
 
<source lang="java">
 
<source lang="java">
 
@Target({TYPE})  
 
@Target({TYPE})  
 
@Retention(RUNTIME)
 
@Retention(RUNTIME)
public @interface XmlExtensible {
+
public @interface XmlVirtualAccessMethods {
  
  String getMethodName() default "get";
+
    /**
  String setMethodName() default "set";
+
    * (Optional) Defines the name of the method used to retrieve virtual properties.
 +
    */
 +
    String getMethod() default "get";
  
  XmlExtensibleSchema schemaGenerationPolicy() default XmlExtensibleSchema.ELEMENTS;
+
    /**
 +
    * (Optional) Defines the name of the method used to store virtual properties.
 +
    */
 +
    String setMethod() default "set";
 +
 
 +
    /**
 +
    * (Optional) Configure the way that virtual properties will appear in generated schemas.<br><br>
 +
    * <b>XmlExtensibleSchema.NODES</b> (default) - Virtual properties will appear as individual nodes<br>
 +
    * <b>XmlExtensibleSchema.ANY</b> - An XSD &lt;any&gt; element will be written to the schema to
 +
    * represent all of the defined virtual properties
 +
    */
 +
    XmlVirtualAccessMethodsSchema schema() default XmlVirtualAccessMethodsSchema.NODES;
 
 
 
}
 
}
 +
</source>
 +
</div>
  
---
+
<div style="width:700px">
 
+
<source lang="java">
public enum XmlExtensibleSchema {
+
public enum XmlVirtualAccessMethodsSchema {
 
     /**
 
     /**
     * XML Extensions are written to the schema as individual elements (default).
+
     * Virtual properties are written to the schema as individual nodes (default).
 
     */
 
     */
     ELEMENTS,
+
     NODES,
  
 
     /**
 
     /**
 
     * An XML <any> element will be written to the schema to represent all
 
     * An XML <any> element will be written to the schema to represent all
     * of the defined Extensions.
+
     * of the defined virtual properties.
 
     */
 
     */
     ANY
+
     ANY;
 
+
 
}
 
}
 
</source>
 
</source>
 
</div>
 
</div>
 +
  
 
== OXM Metadata ==
 
== OXM Metadata ==
Line 71: Line 88:
  
 
<tt>eclipselink_oxm_2_3.xsd</tt>:
 
<tt>eclipselink_oxm_2_3.xsd</tt>:
<div style="width:800px">
+
<div style="width:850px">
 
<source lang="java">
 
<source lang="java">
 
...
 
...
Line 78: Line 95:
 
       <xs:all>
 
       <xs:all>
 
         ...
 
         ...
         <xs:element ref="xml-extensible" minOccurs="0"/>
+
         <xs:element ref="xml-virtual-access-methods" minOccurs="0"/>
 
         ...
 
         ...
  
 
...
 
...
<xs:element name="xml-extensible">
+
    <xs:element name="xml-virtual-access-methods">
  <xs:complexType>
+
        <xs:complexType>
      <xs:attribute name="get-method-name" type="xs:string" default="get" />
+
            <xs:attribute name="get-method" type="xs:string" default="get" />
      <xs:attribute name="set-method-name" type="xs:string" default="set" />
+
            <xs:attribute name="set-method" type="xs:string" default="set" />
      <xs:attribute name="schema-generation-policy" type="xs:string" default="ELEMENTS" />          
+
            <xs:attribute name="schema" type="xml-virtual-access-methods-schema" default="NODES" />
  </xs:complexType>
+
        </xs:complexType>
</xs:element>
+
    </xs:element>
 
...
 
...
 
</source>
 
</source>
 
</div>
 
</div>
 +
  
 
== Example ==
 
== Example ==
  
The following domain class is annotated with <tt>@XmlExtensible</tt>, indicating that it has special accessor methods to handle additional mappings.  EclipseLink's default behaviour will look for the methods <tt>public Object get(String)</tt> and <tt>public void set(String, Object)</tt> to be the accessors of the extensions map.
+
The following domain class is annotated with <tt>@XmlVirtualAccessMethods</tt>, indicating that it has special accessor methods to handle additional mappings.  EclipseLink's default behaviour will look for the methods <tt>public Object get(String)</tt> and <tt>public void set(String, Object)</tt> to be the accessors of the virtual property map.
  
 
<div style="width:700px">
 
<div style="width:700px">
 
<source lang="java">
 
<source lang="java">
 
@XmlRootElement
 
@XmlRootElement
@XmlExtensible
+
@XmlVirtualAccessMethods
 
@XmlAccessorType(AccessType.PROPERTY)
 
@XmlAccessorType(AccessType.PROPERTY)
 
public class Customer {
 
public class Customer {
Line 108: Line 126:
 
   private String name;
 
   private String name;
  
   private Map<String, Object> extensions;
+
   private Map<String, Object> extensions = new HashMap<String, Object>();
  
 
   public Object get(String name) {
 
   public Object get(String name) {
      if (extensions == null) {
 
        extensions = new HashMap<String, Object>();
 
      }
 
 
       return extensions.get(name);
 
       return extensions.get(name);
 
   }
 
   }
 
   
 
   
 
   public void set(String name, Object value) {
 
   public void set(String name, Object value) {
      if (extensions == null) {
 
        extensions = new HashMap<String, Object>();
 
      }
 
 
       extensions.put(name, value);
 
       extensions.put(name, value);
 
   }
 
   }
Line 138: Line 150:
 
...
 
...
 
<java-types>
 
<java-types>
   <java-type name="Customer" xml>
+
   <java-type name="Customer">
 +
      <xml-virtual-access-methods />
 
       <java-attributes>
 
       <java-attributes>
         <xml-attribute java-attribute="name" type="java.lang.String" />
+
         <xml-attribute java-attribute="id" type="java.lang.Integer" />
 +
        <xml-element java-attribute="name" type="java.lang.String" />
 
       </java-attributes>
 
       </java-attributes>
 
   </java-type>
 
   </java-type>
Line 166: Line 180:
 
(Note that there is no special configuration needed for additional mappings; they are specified in the same way as "normal" mappings.)
 
(Note that there is no special configuration needed for additional mappings; they are specified in the same way as "normal" mappings.)
  
To set the values for these additional mappings, we will add values into the <tt>extensions</tt> Map, using the property name as the Map key:
+
To set the values for these additional mappings, we will use the aforementioned <tt>set()</tt> method:
  
 
<div style="width:700px">
 
<div style="width:700px">
Line 194: Line 208:
 
</source>
 
</source>
 
</div>
 
</div>
 +
  
 
== Config Options ==
 
== Config Options ==
 +
  
 
=== Specifying Alternate Accessor Methods ===
 
=== Specifying Alternate Accessor Methods ===
Line 204: Line 220:
 
<source lang="java">
 
<source lang="java">
 
@XmlRootElement
 
@XmlRootElement
@XmlExtensible(getMethodName = "getCustomProps", setMethodName = "putCustomProps")
+
@XmlVirtualAccessMethods(getMethod = "getCustomProps", setMethod = "putCustomProps")
 
@XmlAccessorType(AccessType.FIELD)
 
@XmlAccessorType(AccessType.FIELD)
 
public class Customer {
 
public class Customer {
Line 233: Line 249:
 
</source>
 
</source>
 
</div>
 
</div>
 +
 +
In OXM:
 +
 +
<div style="width:800px">
 +
<source lang="xml">
 +
...
 +
<java-types>
 +
  <java-type name="Customer">
 +
      <xml-virtual-access-methods get-method="getCustomProps" set-method="putCustomProps" />
 +
      <java-attributes>
 +
        <xml-attribute java-attribute="id" type="java.lang.Integer" />
 +
        <xml-element java-attribute="name" type="java.lang.String" />
 +
      </java-attributes>
 +
  </java-type>
 +
...
 +
</source>
 +
</div>
 +
  
 
=== Schema Generation Options ===
 
=== Schema Generation Options ===
  
If the user generates an XML Schema from the <tt>JAXBContext</tt> after extensions have been added, then the resulting schema will obviously be different from any Schema that may have been used to generate the initial domain objects.
+
If the user generates an XML Schema from the <tt>JAXBContext</tt> after virtual properties have been added, then the resulting schema will obviously be different from any Schema that may have been used to generate the initial domain objects.
 +
 
 +
To configure how these new properties should appear in future generated schemas, use the <tt>schema</tt> attribute on <tt>@XmlVirtualAccessMethods</tt>.
  
To configure how these new extensions should appear in future generated schemas, use the <tt>schemaGenerationPolicy</tt> attribute on <tt>@XmlExtensible</tt>.
 
  
'''Extensions as individual Elements'''
+
'''Virtual Properties as individual Nodes'''
  
 
This is EclipseLink's default behaviour, or can be specified explicitly as an override as follows:  
 
This is EclipseLink's default behaviour, or can be specified explicitly as an override as follows:  
Line 247: Line 282:
 
<source lang="java">
 
<source lang="java">
 
@XmlRootElement
 
@XmlRootElement
@XmlExtensible(schemaGenerationPolicy = XmlExtensibleSchema.ELEMENTS)
+
@XmlVirtualAccessMethods(schema = XmlVirtualAccessMethodsSchema.NODES)
 
@XmlAccessorType(AccessType.FIELD)
 
@XmlAccessorType(AccessType.FIELD)
 
public class Customer {
 
public class Customer {
Line 297: Line 332:
 
</div>
 
</div>
  
'''All Extensions in an <any> Element'''
 
  
EclipseLink can also use an <tt><any></tt> element to hold all of the extensions in one node:
+
'''Virtual Properties in an <any> Element'''
 +
 
 +
EclipseLink can also use an <tt><any></tt> element to hold all of the virtual properties in one node:
  
 
<div style="width:700px">
 
<div style="width:700px">
 
<source lang="java">
 
<source lang="java">
 
@XmlRootElement
 
@XmlRootElement
@XmlExtensible(schemaGenerationPolicy = XmlExtensibleSchema.ANY)
+
@XmlVirtualAccessMethods(schema = XmlVirtualAccessMethodsSchema.ANY)
 
@XmlAccessorType(AccessType.FIELD)
 
@XmlAccessorType(AccessType.FIELD)
 
public class Customer {
 
public class Customer {
Line 331: Line 367:
 
</source>
 
</source>
 
</div>
 
</div>
 +
  
 
=== XmlAccessorType and XmlTransient ===
 
=== XmlAccessorType and XmlTransient ===
  
If you are using an <tt>@XmlAccessorType</tt> other than <tt>AccessType.PROPERTY</tt>, you will need to mark your extensions Map attribute to be <tt>@XmlTransient</tt>, to prevent the Map itself from being bound to XML.
+
If you are using an <tt>@XmlAccessorType</tt> other than <tt>AccessType.PROPERTY</tt>, you will need to mark your virtual properties Map attribute to be <tt>@XmlTransient</tt>, to prevent the Map itself from being bound to XML.
  
 
<div style="width:700px">
 
<div style="width:700px">
 
<source lang="java">
 
<source lang="java">
 
@XmlRootElement
 
@XmlRootElement
@XmlExtensible
+
@XmlVirtualAccessMethods
 
@XmlAccessorType(AccessType.FIELD)
 
@XmlAccessorType(AccessType.FIELD)
 
public class Customer {
 
public class Customer {
Line 348: Line 385:
 
</source>
 
</source>
 
</div>
 
</div>
 +
  
 
= Design =
 
= Design =
  
 
* <tt>org.eclipse.persistence.jaxb.compiler.Property</tt>
 
* <tt>org.eclipse.persistence.jaxb.compiler.Property</tt>
** A Property will now know if it is an extensions holder
+
** A Property will now know if it is virtual
 
* <tt>org.eclipse.persistence.jaxb.compiler.TypeInfo</tt>
 
* <tt>org.eclipse.persistence.jaxb.compiler.TypeInfo</tt>
** A TypeInfo can now have an extensions Property
+
** A TypeInfo will now know if it contains virtual properties
* <tt>org.eclipse.persistence.internal.jaxb.XmlExtensionsAttributeAccessor</tt>
+
** New AttributeAccessor to handle getting/setting values from the extensions Map.
+
 
* <tt>org.eclipse.persistence.jaxb.compiler.XMLProcessor</tt>
 
* <tt>org.eclipse.persistence.jaxb.compiler.XMLProcessor</tt>
 
** When processing an OXM file, if a Property is encountered (e.g. "foo") that does not have a corresponding property in Java:
 
** When processing an OXM file, if a Property is encountered (e.g. "foo") that does not have a corresponding property in Java:
*** If the TypeInfo has an extensions Property, then create a new "foo" Property, and setup an XmlExtensionsAttributeAccessor for its mapping
+
*** If the TypeInfo has virtual properties enabled, then create a new "foo" Property, and setup an <tt>org.eclipse.persistence.internal.descriptors.VirtualAttributeAccessor</tt> for its mapping
*** If the TypeInfo does not have an extensions Property, a "No such property exists" exception will be thrown
+
*** If the TypeInfo does not virtual properties enabled, a "No such property exists" exception will be thrown
  
  
Line 393: Line 429:
 
| Rick Barkhouse
 
| Rick Barkhouse
 
| 1.05 : Added XmlExtensionSchemaGenerationPolicy information
 
| 1.05 : Added XmlExtensionSchemaGenerationPolicy information
 +
|-
 +
| 110413
 +
| Rick Barkhouse
 +
| 1.06 : Modified document to reflect new @XmlExtensible design
 +
|-
 +
| 110517
 +
| Rick Barkhouse
 +
| 1.10 : Final design, renamed to @XmlVirtualAccessMethods
 
|}<br>
 
|}<br>

Latest revision as of 12:15, 21 June 2011

Design Documentation: XML Virtual Access Methods

ER 339381

Currently, EclipseLink MOXy supports the mapping of Java fields and properties to XML. Said another way; in order to map data to XML, the user must have an existing Java field or property to map.

To support multi-tenancy, we will be allowing the user to add additional mappings at runtime. Because these new mappings would not have existing fields / properties on the Java class to map to, we will introduce the concept of virtual properties, where we can instead rely on special get() and set() methods to maintain extension data.


Requirements

  1. Users must be able to add new mappings at runtime through EclipseLink OXM
  2. Users should be able to add any type of MOXy mapping as virtual property
  3. Users must be able to specify that a Java type virtual access methods, using either Annotations or EclipseLink OXM


Configuration

In order to add virtual properties to an entity:

  • the Java class must be marked with an @XmlVirtualAccessMethods annotation, or <xml-virtual-access-methods> element in OXM
  • the Java class must contain getter and setter methods to access virtual property values
    • public Object get(String propertyName)
    • public void set(String propertyName, Object value)
    • method names are configurable but must have the same method signatures as above


Idea.png
By default, EclipseLink will look for methods named "set" and "get". To customize accessor method names, see Specifying Alternate Accessor Methods.


Annotations

The user can specify that a Java class may hold virtual properties by using the @XmlVirtualAccessMethods annotation:

@Target({TYPE}) 
@Retention(RUNTIME)
public @interface XmlVirtualAccessMethods {
 
    /**
     * (Optional) Defines the name of the method used to retrieve virtual properties.
     */
    String getMethod() default "get";
 
    /**
     * (Optional) Defines the name of the method used to store virtual properties.
     */
    String setMethod() default "set";
 
    /**
     * (Optional) Configure the way that virtual properties will appear in generated schemas.<br><br>
     * <b>XmlExtensibleSchema.NODES</b> (default) - Virtual properties will appear as individual nodes<br>
     * <b>XmlExtensibleSchema.ANY</b> - An XSD &lt;any&gt; element will be written to the schema to 
     * represent all of the defined virtual properties
     */
    XmlVirtualAccessMethodsSchema schema() default XmlVirtualAccessMethodsSchema.NODES;
 
}
public enum XmlVirtualAccessMethodsSchema {
    /**
     * Virtual properties are written to the schema as individual nodes (default).
     */
    NODES,
 
    /**
     * An XML <any> element will be written to the schema to represent all
     * of the defined virtual properties.
     */
    ANY;
}


OXM Metadata

To indicate an extensions field in EclipseLink OXM, the user can specify an xml-extensible element in their metadata file:

eclipselink_oxm_2_3.xsd:

...
<xs:element name="java-type">
   <xs:complexType>
      <xs:all>
         ...
         <xs:element ref="xml-virtual-access-methods" minOccurs="0"/>
         ...
 
...
    <xs:element name="xml-virtual-access-methods">
        <xs:complexType>
            <xs:attribute name="get-method" type="xs:string" default="get" />
            <xs:attribute name="set-method" type="xs:string" default="set" />
            <xs:attribute name="schema" type="xml-virtual-access-methods-schema" default="NODES" />
        </xs:complexType>
    </xs:element>
...


Example

The following domain class is annotated with @XmlVirtualAccessMethods, indicating that it has special accessor methods to handle additional mappings. EclipseLink's default behaviour will look for the methods public Object get(String) and public void set(String, Object) to be the accessors of the virtual property map.

@XmlRootElement
@XmlVirtualAccessMethods
@XmlAccessorType(AccessType.PROPERTY)
public class Customer {
 
   private int id;
 
   private String name;
 
   private Map<String, Object> extensions = new HashMap<String, Object>();
 
   public Object get(String name) {
      return extensions.get(name);
   }
 
   public void set(String name, Object value) {
      extensions.put(name, value);
   }
 
   @XmlAttribute
   public int getId() {
   ...
 
}

The class above can be expressed in EclipseLink OXM metadata as follows:

...
<java-types>
   <java-type name="Customer">
      <xml-virtual-access-methods />
      <java-attributes>
         <xml-attribute java-attribute="id" type="java.lang.Integer" />
         <xml-element java-attribute="name" type="java.lang.String" />
      </java-attributes>
   </java-type>
...

In a secondary metadata file, we will define additional mappings that we would like to add to Customer:

...
<java-types>
    <java-type name="Customer">
        <java-attributes>
            <xml-element java-attribute="discountCode" name="discount-code"
                type="java.lang.String" />
        </java-attributes>
    </java-type>
</java-types>
...

(Note that there is no special configuration needed for additional mappings; they are specified in the same way as "normal" mappings.)

To set the values for these additional mappings, we will use the aforementioned set() method:

InputStream oxm = classLoader.getResourceAsStream("eclipselink-oxm.xml");
Map<String, Object> properties = new HashMap<String, Object>();
properties.put(JAXBContextFactory.ECLIPSELINK_OXM_XML_KEY, oxm);
 
Class[] classes = new Class[] { Customer.class };
JAXBContext ctx = JAXBContext.newInstance(classes, properties);
 
Customer c = new Customer();
c.setName("Dan Swano");
c.set("discountCode", "SIUB372JS7G2IUDS7");
 
ctx.createMarshaller().marshal(e, System.out);

This will produce the following XML:

<customer name="Dan Swano">
   <discount-code>SIUB372JS7G2IUDS7</discount-code>
</customer>


Config Options

Specifying Alternate Accessor Methods

To use different method names as your extensions accessors, specify them using the getMethodName and setMethodName attributes on @XmlExtensible:

@XmlRootElement
@XmlVirtualAccessMethods(getMethod = "getCustomProps", setMethod = "putCustomProps")
@XmlAccessorType(AccessType.FIELD)
public class Customer {
 
   @XmlAttribute
   private int id;
 
   private String name;
 
   @XmlTransient
   private Map<String, Object> extensions;
 
   public Object getCustomProps(String name) {
      if (extensions == null) {
         extensions = new HashMap<String, Object>();
      }
      return extensions.get(name);
   }
 
   public void putCustomProps(String name, Object value) {
      if (extensions == null) {
         extensions = new HashMap<String, Object>();
      }
      extensions.put(name, value);
   }
 
}

In OXM:

...
<java-types>
   <java-type name="Customer">
      <xml-virtual-access-methods get-method="getCustomProps" set-method="putCustomProps" />
      <java-attributes>
         <xml-attribute java-attribute="id" type="java.lang.Integer" />
         <xml-element java-attribute="name" type="java.lang.String" />
      </java-attributes>
   </java-type>
...


Schema Generation Options

If the user generates an XML Schema from the JAXBContext after virtual properties have been added, then the resulting schema will obviously be different from any Schema that may have been used to generate the initial domain objects.

To configure how these new properties should appear in future generated schemas, use the schema attribute on @XmlVirtualAccessMethods.


Virtual Properties as individual Nodes

This is EclipseLink's default behaviour, or can be specified explicitly as an override as follows:

@XmlRootElement
@XmlVirtualAccessMethods(schema = XmlVirtualAccessMethodsSchema.NODES)
@XmlAccessorType(AccessType.FIELD)
public class Customer {
 
   ...

For example:

Original Customer Schema:

<xs:schema ...>
 
    <xs:element name="customer">
        <xs:complexType>
            <xs:sequence>
                <xs:element name="first-name" type="xs:string" />
                <xs:element name="last-name" type="xs:string" />
            </xs:sequence>
        </xs:complexType>
    </xs:element>
 
</xs:schema>

Generated Schema after adding middle-initial and phone-number:

<xs:schema ...>
 
    <xs:element name="customer">
        <xs:complexType>
            <xs:sequence>
                <xs:element name="first-name" type="xs:string" />
                <xs:element name="last-name" type="xs:string" />
                <xs:element name="middle-initial" type="xs:string" />
                <xs:element name="phone-number" type="xs:string" />
            </xs:sequence>
        </xs:complexType>
    </xs:element>
 
</xs:schema>


Virtual Properties in an <any> Element

EclipseLink can also use an <any> element to hold all of the virtual properties in one node:

@XmlRootElement
@XmlVirtualAccessMethods(schema = XmlVirtualAccessMethodsSchema.ANY)
@XmlAccessorType(AccessType.FIELD)
public class Customer {
 
   ...

Taking the example from above, a newly generated schema using this approach would look like:

<xs:schema ...>
 
    <xs:element name="customer">
        <xs:complexType>
            <xs:sequence>
                <xs:element name="first-name" type="xs:string" />
                <xs:element name="last-name" type="xs:string" />
                <xs:any minOccurs="0" />
            </xs:sequence>
        </xs:complexType>
    </xs:element>
 
</xs:schema>


XmlAccessorType and XmlTransient

If you are using an @XmlAccessorType other than AccessType.PROPERTY, you will need to mark your virtual properties Map attribute to be @XmlTransient, to prevent the Map itself from being bound to XML.

@XmlRootElement
@XmlVirtualAccessMethods
@XmlAccessorType(AccessType.FIELD)
public class Customer {
 
   @XmlTransient
   private Map<String, Object> extensions;
   ...


Design

  • org.eclipse.persistence.jaxb.compiler.Property
    • A Property will now know if it is virtual
  • org.eclipse.persistence.jaxb.compiler.TypeInfo
    • A TypeInfo will now know if it contains virtual properties
  • org.eclipse.persistence.jaxb.compiler.XMLProcessor
    • When processing an OXM file, if a Property is encountered (e.g. "foo") that does not have a corresponding property in Java:
      • If the TypeInfo has virtual properties enabled, then create a new "foo" Property, and setup an org.eclipse.persistence.internal.descriptors.VirtualAttributeAccessor for its mapping
      • If the TypeInfo does not virtual properties enabled, a "No such property exists" exception will be thrown


Document History

Date Author Version Description & Notes
110323 Rick Barkhouse 1.00
110329 Rick Barkhouse 1.01 : Input from Doug, added Action Items
110331 Rick Barkhouse 1.02 : Moved open items to Discussion page
110404 Rick Barkhouse 1.03 : Changed to "XML Flex Extensions", modified OXM configuration
110406 Rick Barkhouse 1.04 : Changed to "XML Extensions", added Schema Generation section
110407 Rick Barkhouse 1.05 : Added XmlExtensionSchemaGenerationPolicy information
110413 Rick Barkhouse 1.06 : Modified document to reflect new @XmlExtensible design
110517 Rick Barkhouse 1.10 : Final design, renamed to @XmlVirtualAccessMethods

Back to the top