ExtensibleBase
The @XmlVirtualAccessMethods annotation is used to specify that a class is extensible. An extensible class is required to have a "get" method that returns a value by property name, and a "set" method that stores a value by property name. The default names for these methods are "get" and "set", and can be overridden with the @XmlVirtualAccessMethods annotation. Since we will have multiple extensible classes in this example we'll configure a base class for this behaviour that extensible classes can extend. We will use the @XmlTransient annotation to prevent ExtensibleBase from being mapped as an inheritance relationship.
package blog.virtual; import java.util.HashMap; import java.util.Map; import javax.xml.bind.annotation.XmlTransient; import org.eclipse.persistence.oxm.annotations.XmlVirtualAccessMethods; @XmlTransient @XmlVirtualAccessMethods(setMethod="put") public class ExtensibleBase { private Map<String, Object> extensions = new HashMap<String, Object>(); public <T> T get(String property) { return (T) extensions.get(property); } public void put(String property, Object value) { extensions.put(property, value); } }
Customer
The Customer class will be extensible since it inherits from a domain class that has been annotated with @XmlVirtualAccessMethods.
package blog.virtual; import javax.xml.bind.annotation.XmlRootElement; @XmlRootElement public class Customer extends ExtensibleBase { private String firstName; private String lastName; private Address billingAddress; public String getFirstName() { return firstName; } public void setFirstName(String firstName) { this.firstName = firstName; } public String getLastName() { return lastName; } public void setLastName(String lastName) { this.lastName = lastName; } public Address getBillingAddress() { return billingAddress; } public void setBillingAddress(Address billingAddress) { this.billingAddress = billingAddress; } }
Address
package blog.virtual; public class Address { private String street; public String getStreet() { return street; } public void setStreet(String street) { this.street = street; } }
PhoneNumber
PhoneNumber like Customer will be an extensible class.
package blog.virtual; import javax.xml.bind.annotation.XmlValue; public class PhoneNumber extends ExtensibleBase { private String number; @XmlValue public String getNumber() { return number; } public void setNumber(String number) { this.number = number; } }
Mapping File
The metadata for the virtual properties is supplied through MOXy's XML mapping file. Virtual properties are mapped in the same way as real properties. Some additional information is required including type (since this can not be determined via reflection), and for collection properties a container type.
<?xml version="1.0"?> <xml-bindings xmlns="http://www.eclipse.org/eclipselink/xsds/persistence/oxm" package-name="blog.virtual"> <java-types> <java-type name="Customer"> <xml-type prop-order="firstName middleName lastName billingAddress shippingAddress phoneNumbers"/> <java-attributes> <xml-attribute java-attribute="id" type="java.lang.Integer"/> <xml-element java-attribute="middleName" type="java.lang.String"/> <xml-element java-attribute="shippingAddress" type="blog.virtual.Address"/> <xml-element java-attribute="phoneNumbers" name="phoneNumber" type="blog.virtual.PhoneNumber" container-type="java.util.List"/> </java-attributes> </java-type> <java-type name="PhoneNumber"> <java-attributes> <xml-attribute java-attribute="type" type="java.lang.String"/> </java-attributes> </java-type> </java-types> </xml-bindings>
Demo
The get/set methods are used on the domain model to interact with the real properties and the accessors defined on the @XmlVirtualAccessMethods annotation are used to interact with the virtual properties. The normal JAXB mechanisms are used for marshal and unmarshal operations:
package blog.virtual; import java.io.File; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.xml.bind.JAXBContext; import javax.xml.bind.Marshaller; import javax.xml.bind.Unmarshaller; import org.eclipse.persistence.jaxb.JAXBContextFactory; public class Demo { public static void main(String[] args) throws Exception { Map<String, Object> properties = new HashMap<String, Object>(); properties.put(JAXBContextFactory.ECLIPSELINK_OXM_XML_KEY, "blog/virtual/binding.xml"); JAXBContext jc = JAXBContext.newInstance(new Class[] {Customer.class, Address.class}, properties); Unmarshaller unmarshaller = jc.createUnmarshaller(); File xml = new File("src/blog/virtual/input.xml"); Customer customer = (Customer) unmarshaller.unmarshal(xml); customer.setLastName("Doe"); customer.put("middleName", "Anne"); PhoneNumber cellPhoneNumber = new PhoneNumber(); cellPhoneNumber.setNumber("555-CELL"); cellPhoneNumber.put("type", "CELL"); customer.<List<PhoneNumber>>get("phoneNumbers").add(cellPhoneNumber); Marshaller marshaller = jc.createMarshaller(); marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); marshaller.marshal(customer, System.out); } }
XML
As you can see below there is nothing special in XML related to real or virtual properties:
Input (input.xml)
<?xml version="1.0" encoding="UTF-8"?> <customer id="123"> <firstName>Jane</firstName> <lastName>Doh</lastName> <billingAddress> <street>1 Billing Street</street> </billingAddress> <shippingAddress> <street>2 Shipping Road</street> </shippingAddress> <phoneNumber type="WORK">555-WORK</phoneNumber> <phoneNumber type="HOME">555-HOME</phoneNumber> </customer>
Output
<?xml version="1.0" encoding="UTF-8"?> <customer id="123"> <firstName>Jane</firstName> <middleName>Anne</middleName> <lastName>Doe</lastName> <billingAddress> <street>1 Billing Street</street> </billingAddress> <shippingAddress> <street>2 Shipping Road</street> </shippingAddress> <phoneNumber type="WORK">555-WORK</phoneNumber> <phoneNumber type="HOME">555-HOME</phoneNumber> <phoneNumber type="CELL">555-CELL</phoneNumber> </customer>
Further Reading
If you enjoyed this post then you may also be interested in:
Other articles related to new MOXy features in EclipseLink 2.3:
No comments:
Post a Comment
Note: Only a member of this blog may post a comment.