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.
Difference between revisions of "EclipseLink/Development/339381"
Line 1: | Line 1: | ||
− | + | {{EclipseLink_UserGuide | |
+ | |eclipselink=y | ||
+ | |eclipselinktype=MOXy | ||
+ | |info=y | ||
+ | |api=y | ||
+ | |apis= * [http://www.eclipse.org/eclipselink/api/latest/org/eclipse/persistence/oxm/annotations/XmlVirtualAccessMethods.html XmlVirtualAccessMethods] | ||
+ | |toc=y | ||
+ | }} | ||
− | = | + | = Virtual Access Methods = |
− | + | In addition to the standard JAXB access methods (public member, field, property, etc.), EclipseLink MOXy 2.3 introduces the concept of virtual properties and virtual access methods, which instead rely on special '''get()''' and '''set()''' methods to maintain mapping data. For example, you might want to use a '''HashMap''' as the underlying structure to hold data for certain mappings. The mappings that use virtual method access must be defined in EclipseLink OXM metadata. | |
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
In order to add virtual properties to an entity: | In order to add virtual properties to an entity: | ||
− | * the Java class must be marked with an | + | * 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 | * 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 | ** method names are configurable but must have the same method signatures as above | ||
Line 33: | Line 26: | ||
− | == | + | == Configuration == |
− | + | Virtual Access Methods can be configured either through Java annotations or EclipseLink OXM metadata. | |
− | + | === Using Annotations === | |
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
<div style="width:700px"> | <div style="width:700px"> | ||
<source lang="java"> | <source lang="java"> | ||
− | + | package example; | |
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | import javax.xml.bind.annotation.*; | |
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
+ | import org.eclipse.persistence.oxm.annotations.XmlVirtualAccessMethods; | ||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
@XmlRootElement | @XmlRootElement | ||
@XmlVirtualAccessMethods | @XmlVirtualAccessMethods | ||
@XmlAccessorType(AccessType.PROPERTY) | @XmlAccessorType(AccessType.PROPERTY) | ||
public class Customer { | public class Customer { | ||
− | + | ||
private int id; | private int id; | ||
− | + | ||
private String name; | private String name; | ||
− | + | ||
private Map<String, Object> extensions = new HashMap<String, Object>(); | private Map<String, Object> extensions = new HashMap<String, Object>(); | ||
− | + | ||
public Object get(String name) { | public Object get(String name) { | ||
return extensions.get(name); | return extensions.get(name); | ||
Line 135: | Line 58: | ||
extensions.put(name, value); | extensions.put(name, value); | ||
} | } | ||
− | + | ||
@XmlAttribute | @XmlAttribute | ||
public int getId() { | public int getId() { | ||
... | ... | ||
− | + | ||
} | } | ||
</source> | </source> | ||
</div> | </div> | ||
− | + | === Using EclipseLink OXM === | |
<div style="width:700px"> | <div style="width:700px"> | ||
Line 153: | Line 76: | ||
<xml-virtual-access-methods /> | <xml-virtual-access-methods /> | ||
<java-attributes> | <java-attributes> | ||
− | <xml-attribute java-attribute="id | + | <xml-attribute java-attribute="id"/> |
− | <xml-element java-attribute="name | + | <xml-element java-attribute="name"/> |
</java-attributes> | </java-attributes> | ||
</java-type> | </java-type> | ||
Line 161: | Line 84: | ||
</div> | </div> | ||
− | |||
+ | == Example == | ||
+ | |||
+ | For this example we will use the following '''Employee''' class. In addition to some conventional JAXB mappings, we also specify that this class contains virtual properties by including the '''@XmlVirtualAccessMethods''' annotation (or alternately in OXM). | ||
+ | |||
+ | Next, we define our virtual mappings in their own EclipseLink OXM file. Any property encountered in this file that does not have a corresponding Java attribute will be considered a virtual property and will be accessed through the virtual access methods. | ||
+ | |||
+ | '''virtualprops-oxm.xml''' | ||
<div style="width:700px"> | <div style="width:700px"> | ||
<source lang="xml"> | <source lang="xml"> | ||
Line 178: | Line 107: | ||
</div> | </div> | ||
− | + | When creating the '''JAXBContext''', we pass in the '''virtualprops''' mappings along with our '''Customer''' class. | |
− | To set the values for | + | To set the values for virtual properties, we will use the aforementioned '''set()''' method. |
<div style="width:700px"> | <div style="width:700px"> | ||
Line 210: | Line 139: | ||
− | == | + | |
+ | == 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. | ||
+ | |||
+ | <div style="width:700px"> | ||
+ | <source lang="java"> | ||
+ | package example; | ||
+ | |||
+ | @XmlRootElement | ||
+ | @XmlVirtualAccessMethods | ||
+ | @XmlAccessorType(AccessType.FIELD) | ||
+ | public class Customer { | ||
+ | |||
+ | @XmlTransient | ||
+ | private Map<String, Object> extensions; | ||
+ | ... | ||
+ | </source> | ||
+ | </div> | ||
+ | |||
+ | |||
+ | == Configuration Options == | ||
=== Specifying Alternate Accessor Methods === | === Specifying Alternate Accessor Methods === | ||
− | To use different method names as your | + | To use different method names as your virtual method accessors, specify them using the '''getMethodName''' and '''setMethodName''' attributes on '''@XmlVirtualAccessMethods''': |
<div style="width:700px"> | <div style="width:700px"> | ||
<source lang="java"> | <source lang="java"> | ||
+ | package example; | ||
+ | |||
@XmlRootElement | @XmlRootElement | ||
@XmlVirtualAccessMethods(getMethod = "getCustomProps", setMethod = "putCustomProps") | @XmlVirtualAccessMethods(getMethod = "getCustomProps", setMethod = "putCustomProps") | ||
Line 270: | Line 222: | ||
=== Schema Generation Options === | === Schema Generation Options === | ||
− | If the user generates an XML Schema from the | + | 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 | + | To configure how these new properties should appear in future generated schemas, use the '''schema''' attribute on '''@XmlVirtualAccessMethods'''. |
Line 281: | Line 233: | ||
<div style="width:700px"> | <div style="width:700px"> | ||
<source lang="java"> | <source lang="java"> | ||
+ | package example; | ||
+ | |||
@XmlRootElement | @XmlRootElement | ||
@XmlVirtualAccessMethods(schema = XmlVirtualAccessMethodsSchema.NODES) | @XmlVirtualAccessMethods(schema = XmlVirtualAccessMethodsSchema.NODES) | ||
Line 292: | Line 246: | ||
For example: | For example: | ||
− | Original | + | Original '''Customer''' Schema: |
<div style="width:700px"> | <div style="width:700px"> | ||
Line 311: | Line 265: | ||
</div> | </div> | ||
− | Generated Schema after adding | + | Generated Schema after adding '''middle-initial''' and '''phone-number''': |
<div style="width:700px"> | <div style="width:700px"> | ||
Line 335: | Line 289: | ||
'''Virtual Properties in an <any> Element''' | '''Virtual Properties in an <any> Element''' | ||
− | EclipseLink can also use an | + | EclipseLink can also use an '''<any>''' 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"> | ||
+ | package example; | ||
+ | |||
@XmlRootElement | @XmlRootElement | ||
@XmlVirtualAccessMethods(schema = XmlVirtualAccessMethodsSchema.ANY) | @XmlVirtualAccessMethods(schema = XmlVirtualAccessMethodsSchema.ANY) | ||
Line 367: | Line 323: | ||
</source> | </source> | ||
</div> | </div> | ||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− |
Revision as of 12:14, 21 June 2011
EclipseLink MOXy
EclipseLink | |
Website | |
Download | |
Community | |
Mailing List • Forums • IRC • mattermost | |
Issues | |
Open • Help Wanted • Bug Day | |
Contribute | |
Browse Source |
Key API
Contents
Virtual Access Methods
In addition to the standard JAXB access methods (public member, field, property, etc.), EclipseLink MOXy 2.3 introduces the concept of virtual properties and virtual access methods, which instead rely on special get() and set() methods to maintain mapping data. For example, you might want to use a HashMap as the underlying structure to hold data for certain mappings. The mappings that use virtual method access must be defined in EclipseLink OXM metadata.
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
Configuration
Virtual Access Methods can be configured either through Java annotations or EclipseLink OXM metadata.
Using Annotations
package example; import javax.xml.bind.annotation.*; import org.eclipse.persistence.oxm.annotations.XmlVirtualAccessMethods; @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() { ... }
Using EclipseLink OXM
... <java-types> <java-type name="Customer"> <xml-virtual-access-methods /> <java-attributes> <xml-attribute java-attribute="id"/> <xml-element java-attribute="name"/> </java-attributes> </java-type> ...
Example
For this example we will use the following Employee class. In addition to some conventional JAXB mappings, we also specify that this class contains virtual properties by including the @XmlVirtualAccessMethods annotation (or alternately in OXM).
Next, we define our virtual mappings in their own EclipseLink OXM file. Any property encountered in this file that does not have a corresponding Java attribute will be considered a virtual property and will be accessed through the virtual access methods.
virtualprops-oxm.xml
... <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> ...
When creating the JAXBContext, we pass in the virtualprops mappings along with our Customer class.
To set the values for virtual properties, 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>
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.
package example; @XmlRootElement @XmlVirtualAccessMethods @XmlAccessorType(AccessType.FIELD) public class Customer { @XmlTransient private Map<String, Object> extensions; ...
Configuration Options
Specifying Alternate Accessor Methods
To use different method names as your virtual method accessors, specify them using the getMethodName and setMethodName attributes on @XmlVirtualAccessMethods:
package example; @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:
package example; @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:
package example; @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>