In a previous blog post I wrote about how to map to the choice structure in XML schema when starting from classes. An astute reader tried generating an object model from that schema and noticed that the JAXB implementation generated something different than expected. In this post I'll explain the difference.
XML Schema (customer.xsd)
In this XML schema the choice structure means that after the name element, one of the following elements may occur: address, phone-number, or note.
<?xml version="1.0" encoding="UTF-8"?> <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <xsd:element name="customer" type="customer"/> <xsd:complexType name="customer"> <xsd:sequence> <xsd:element name="name" type="xsd:string" minOccurs="0"/> <xsd:choice> <xsd:element name="address" type="address"/> <xsd:element name="phone-number" type="phoneNumber"/> <xsd:element name="note" type="xsd:string"/> </xsd:choice> </xsd:sequence> </xsd:complexType> <xsd:complexType name="address"> <xsd:sequence> <xsd:element name="city" type="xsd:string" minOccurs="0"/> <xsd:element name="street" type="xsd:string" minOccurs="0"/> </xsd:sequence> </xsd:complexType> <xsd:complexType name="phoneNumber"> <xsd:simpleContent> <xsd:extension base="xsd:string"> <xsd:attribute name="type" type="xsd:string"/> </xsd:extension> </xsd:simpleContent> </xsd:complexType> </xsd:schema>
Default Generated Class
xjc -d out -p com.example customer.xsd
If we generate Java classes from our schema using the above command, then we will get a Customer class that looks something like the following (accessors have been omitted to save space). What is surprising here is that JAXB has generated a property for each option in the choice structure.
package com.example; import javax.xml.bind.annotation.XmlAccessType; import javax.xml.bind.annotation.XmlAccessorType; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlType; @XmlAccessorType(XmlAccessType.FIELD) @XmlType(name = "customer", propOrder = { "name", "address", "phoneNumber", "note" }) public class Customer { protected String name; protected Address address; @XmlElement(name = "phone-number") protected PhoneNumber phoneNumber; protected String note; }
Binding File (binding.xml)
We can override the behaviour observed above through the use of a JAXB binding file. In this file we will specify that we want choice structures mapped to a choice property mapped with @XmlElements.
<?xml version="1.0" encoding="UTF-8"?> <bindings xmlns="http://java.sun.com/xml/ns/jaxb" version="2.1"> <globalBindings choiceContentProperty="true"/> </bindings>
Generated Class (using Binding File)
xjc -d out -p com.example -b binding.xml customer.xsd
Now if we generate Java classes from our schema using the above command (which specifies the binding file), we will get a Customer class that looks something like the following (again accessors have been omitted to save space). We see that we have one property that corresponds to the choice structure that is annotated with @XmlElements.
package com.example; import javax.xml.bind.annotation.XmlAccessType; import javax.xml.bind.annotation.XmlAccessorType; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlElements; import javax.xml.bind.annotation.XmlType; @XmlAccessorType(XmlAccessType.FIELD) @XmlType(name = "customer", propOrder = { "name", "addressOrPhoneNumberOrNote" }) public class Customer { protected String name; @XmlElements({ @XmlElement(name="phone-number", type=PhoneNumber.class), @XmlElement(name="address", type=Address.class), @XmlElement(name="note", type=String.class) }) protected Object addressOrPhoneNumberOrNote; }
Further Reading
If you enjoyed this post you may also be interested in:
Blaise Doughan,
ReplyDeleteThis was great help to me, but still In RAD I have small issue with sequence tag have for choice. By closing sequence after name element its working fine.
Thanks for your help