Jump to: navigation, search

EclipseLink/Examples/MOXy/Extensible

< EclipseLink‎ | Examples‎ | MOXy
Revision as of 15:20, 2 June 2011 by Denise.mahar.oracle.com (Talk | contribs)

(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)

Introduction

JAXB is designed to work with domain models that have real fields/properties. In EclipseLink 2.3, MOXy introduces the concept of virtual properties. Virtual properties are defined by the MOXy metadata file, and provide a way to extend a class without modifying the source.

Java Model with @XmlVirtualAccessMethods annotation

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.

ExtensibleBase

package examples.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 examples.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

It is not necessary to have every class in your model be extensible. In this example the Address class will not have any virtual properties.

package examples.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 examples.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;
    }
 
}

External Bindings 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. Virtual properties may also be referred to in the prop-order. The virtual properties defined below for Customer are "middleName", "shippingAddress" and "phoneNumbers". For PhoneNumber the virtual property is the "type" property.

<?xml version="1.0"?>
<xml-bindings
    xmlns="http://www.eclipse.org/eclipselink/xsds/persistence/oxm"
    package-name="examples.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="examples.virtual.Address"/>
                <xml-element java-attribute="phoneNumbers" name="phoneNumber" type="examples.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>

Usage

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:

Customer customer = new Customer();
//Set Customer's real properties
customer.setFirstName("Jane")
customer.setLastName("Doe")
Address realBillingAddress = new Address();
realBillingAddress.setStreet("1 Billing Street")
customer.setBillingAddress(realBillingAddress);
 
//Set Customer's virtual 'shippingAddress' property
Address virtualShippingAddress = new Address();
virtualShippingAddress.setStreet("2 Shipping Road")
customer.put("shippingAddress", virtualShippingAddress);
 
//Set Customer's virtual 'middleName' property
customer.put("middleName", "Anne");
 
PhoneNumber cellPhoneNumber = new PhoneNumber();
cellPhoneNumber.setNumber("555-CELL");
//Set the PhoneNumber's virtual 'type' property
cellPhoneNumber.put("type", "CELL");
customer.<List<PhoneNumber>>get("phoneNumbers").add(cellPhoneNumber);

When the Customer object is converted to XML there is nothing special in resulting XML related to real or virtual properties:

Map<String, Object> properties = new HashMap<String, Object>();
properties.put(JAXBContextFactory.ECLIPSELINK_OXM_XML_KEY, "examples/virtual/binding.xml");
JAXBContext jc = JAXBContext.newInstance(new Class[] {Customer.class, Address.class}, properties);
 
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(customer, System.out);

Resulting XML

<?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="CELL">555-CELL</phoneNumber>
</customer>