Difference between revisions of "EclipseLink/UserGuide/MOXy/Type Level/Handling Inheritance"

From Eclipsepedia

Jump to: navigation, search
m (Using MOXy Exentions: @XmlDescriminatorNode/@XmlDescrimintatorValue)
m (Replacing page with 'Please see http://www.eclipse.org/eclipselink/documentation/2.4/moxy/type_level003.htm')
 
(18 intermediate revisions by 2 users not shown)
Line 1: Line 1:
= Handling Inheritance =
+
Please see http://www.eclipse.org/eclipselink/documentation/2.4/moxy/type_level003.htm
 
+
* [[#xsitype|xsi:Type Attribute]]
+
* [[#substitution|Substitution Groups]]
+
* [[#moxyextensions|MOXy Extension @XmlDescriminatorNode/@XmlDescrimintatorValue]]
+
 
+
 
+
<span id="xsitype"></span>
+
== Using xsi:type Attribute ==
+
 
+
You can use the '''xsi:type''' attribute to represent inheritance in JAXB.
+
 
+
In this example an abstract super class ('''ContactInfo'') contains all types of contact information. '''Address''' and '''PhoneNumber''' are the concrete implementations of '''ContactInfo'''.
+
 
+
<source lang="java">
+
package blog.inheritance;
+
+
public abstract class ContactInfo {
+
+
}
+
 
+
public class Address extends ContactInfo {
+
+
    private String street;
+
+
    public String getStreet() {
+
        return street;
+
    }
+
+
    public void setStreet(String street) {
+
        this.street = street;
+
    }
+
+
}
+
 
+
public class PhoneNumber extends ContactInfo {
+
+
}
+
 
+
</source>
+
 
+
 
+
Because the '''Customer''' object can have different types of contact information, its property refers to the superclass.
+
 
+
<source lang="java">
+
package blog.inheritance;
+
+
import javax.xml.bind.annotation.XmlRootElement;
+
+
@XmlRootElement
+
public class Customer {
+
+
    private ContactInfo contactInfo;
+
+
    public ContactInfo getContactInfo() {
+
        return contactInfo;
+
    }
+
+
    public void setContactInfo(ContactInfo contactInfo) {
+
        this.contactInfo = contactInfo;
+
    }
+
+
}
+
</source>
+
 
+
 
+
 
+
In this example, the '''xsi:type''' attribute represents inheritance.
+
 
+
<source lang="java">
+
package blog.inheritance;
+
+
import javax.xml.bind.JAXBContext;
+
import javax.xml.bind.Marshaller;
+
+
public class Demo {
+
+
    public static void main(String[] args) throws Exception {
+
        Customer customer = new Customer();
+
        Address address = new Address();
+
        address.setStreet("1 A Street");
+
        customer.setContactInfo(address);
+
+
        JAXBContext jc = JAXBContext.newInstance(Customer.class, Address.class, PhoneNumber.class);
+
+
        Marshaller marshaller = jc.createMarshaller();
+
        marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
+
        marshaller.marshal(customer, System.out);
+
    }
+
+
}
+
</source>
+
 
+
The above sample code produces the following XML.
+
<source lang="xml">
+
+
<customer>
+
    <contactInfo
+
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+
    xsi:type="address">
+
        <street>1 A Street</street>
+
    </contactInfo>
+
</customer>
+
</source>
+
Note the '''xsi:type''' attribute on the '''contactInfo''' element.
+
 
+
 
+
<span id="substitution"></span>
+
==Using Substitution Groups==
+
You can represent inheritance by using the element name with XML schema ''substitution groups''.
+
 
+
In this example, the Java model contains an abstract superclass, '''ContactInfo''' for all types of contact information. '''Address''' and '''PhoneNumber''' are the concrete implementations of '''ContactInfo'''. 
+
 
+
<source lang="java">
+
+
package blog.inheritance;
+
+
import javax.xml.bind.annotation.XmlRootElement;
+
+
public abstract class ContactInfo {
+
+
}
+
 
+
@XmlRootElement
+
public class Address extends ContactInfo {
+
+
    private String street;
+
+
    public String getStreet() {
+
        return street;
+
    }
+
+
    public void setStreet(String street) {
+
        this.street = street;
+
    }
+
+
}
+
 
+
@XmlRootElement
+
public class PhoneNumber extends ContactInfo {
+
+
}
+
 
+
</source>
+
+
Both '''Address''' and '''PhoneNumber''' use the '''@XmlRootElement''' annotation because the element name is used as the inheritance indicator.
+
 
+
 
+
Because the '''Customer''' object can have different types of contact information, its property refers to the superclass. The '''contactInfo''' property includes the '''@XmlElementRef''' annotation to indicate the value type will be derived from the element name (and namespace URI).
+
 
+
<source lang="java">
+
package blog.inheritance;
+
+
import javax.xml.bind.annotation.XmlElementRef;
+
import javax.xml.bind.annotation.XmlRootElement;
+
+
@XmlRootElement
+
public class Customer {
+
+
    private ContactInfo contactInfo;
+
+
    @XmlElementRef
+
    public ContactInfo getContactInfo() {
+
        return contactInfo;
+
    }
+
+
    public void setContactInfo(ContactInfo contactInfo) {
+
        this.contactInfo = contactInfo;
+
    }
+
+
}
+
 
+
</source>
+
 
+
In the following example, the '''xsi:type''' attribute represents inheritance.
+
 
+
<source lang="java">
+
+
package blog.inheritance;
+
+
import javax.xml.bind.JAXBContext;
+
import javax.xml.bind.Marshaller;
+
+
public class Demo {
+
+
    public static void main(String[] args) throws Exception {
+
        Customer customer = new Customer();
+
        Address address = new Address();
+
        address.setStreet("1 A Street");
+
        customer.setContactInfo(address);
+
+
        JAXBContext jc = JAXBContext.newInstance(Customer.class, Address.class, PhoneNumber.class);
+
+
        Marshaller marshaller = jc.createMarshaller();
+
        marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
+
        marshaller.marshal(customer, System.out);
+
    }
+
+
}
+
</source>
+
 
+
The above sample code produces the following XML.
+
<source lang="xml">
+
 
+
<customer>
+
    <address>
+
        <street>1 A Street</street>
+
    </address>
+
</customer>
+
 
+
</source>
+
 
+
Note that the '''Address''' object is marshalled to the '''address''' element.
+
 
+
 
+
 
+
 
+
 
+
 
+
 
+
 
+
 
+
 
+
 
+
 
+
 
+
<span id="moxyextensions"></span>
+
==Using MOXy Exentions:  @XmlDescriminatorNode/@XmlDescrimintatorValue ==
+
You can use the '''@XmlDescriminatorNode''' and '''@XmlDescrimintatorValue''' MOXy extensions avaialable in EclipseLink 2.2 JAXB to represent inheritance. With these extensions, you can select the attribute to represent the subtype.
+
 
+
In this example an abstract super class ('''ContactInfo'') contains all types of contact information. The '''ContactInfo''' uses the '''@XmlDescriminatorNode''' annotation to specify the XML attribute ('''classifier''') to indicate the subtype.
+
 
+
'''Address''' and '''PhoneNumber''' are the concrete implementations of '''ContactInfo'''. The '''@XmlDescriminatorValue''' is used to override the default type name.
+
 
+
<source lang="java">
+
package blog.inheritance;
+
+
import org.eclipse.persistence.oxm.annotations.XmlDiscriminatorNode;
+
import org.eclipse.persistence.oxm.annotations.XmlDiscriminatorValue;
+
 
+
@XmlDiscriminatorNode("@classifier")
+
public abstract class ContactInfo {
+
+
}
+
 
+
@XmlDiscriminatorValue("address-classifier")
+
public class Address extends ContactInfo {
+
+
    private String street;
+
+
    public String getStreet() {
+
        return street;
+
    }
+
+
    public void setStreet(String street) {
+
        this.street = street;
+
    }
+
+
}
+
 
+
@XmlDiscriminatorValue("phone-number-classifier")
+
public class PhoneNumber extends ContactInfo {
+
+
}
+
 
+
</source>
+
+
 
+
The''' Customer''' object can have different types of contact info set on it, so the property will refer to the super class.
+
===jaxb.properties===
+
To use the MOXy JAXB implementation, use a '''jaxb.properties''' file with the '''Address''' class that contains the following:
+
<code>
+
javax.xml.bind.context.factory=org.eclipse.persistence.jaxb.JAXBContextFactory
+
</code>
+
 
+
We will use the following demo code to demonstrate the use of the descriminator node to represent inheritance.
+
 
+
<source lang="java">
+
+
package blog.inheritance;
+
+
import javax.xml.bind.JAXBContext;
+
import javax.xml.bind.Marshaller;
+
+
public class Demo {
+
+
    public static void main(String[] args) throws Exception {
+
        Customer customer = new Customer();
+
        Address address = new Address();
+
        address.setStreet("1 A Street");
+
        customer.setContactInfo(address);
+
+
        JAXBContext jc = JAXBContext.newInstance(Customer.class, Address.class, PhoneNumber.class);
+
+
        Marshaller marshaller = jc.createMarshaller();
+
        marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
+
        marshaller.marshal(customer, System.out);
+
    }
+
+
}
+
</source>
+
 
+
The following is the resulting XML document.  Note that the Address object is marshalled to the contactInfo element with the classifier attribute containing the discriminator node value "address-classifier".
+
 
+
<source lang="xml">
+
 
+
<customer>
+
  <contactInfo classifier="address-classifier">
+
      <street>1 A Street</street>
+
  </contactInfo>
+
</customer>
+
 
+
</source>
+
 
+
This is very similar to using the xsi:type attribute to represent the sub type.  However in this case we are not leveraging an XML schema concept.  This is not necessarily a bad thing, as there are legitimate use cases where this type of behaviour is required (check out this Stack Overflow post for an example).  It is also useful when you need to interact with XML binding tools with proprietary mechanisms for handling inheritance.
+

Latest revision as of 09:50, 8 November 2012

Please see http://www.eclipse.org/eclipselink/documentation/2.4/moxy/type_level003.htm