I have 4 classes. Person
class, and abstract ContactInformation
with Phone
and Address
class extending it.
@XmlRootElement
@XmlAccessorType(XmlAccessType.NONE)
public class Person {
@XmlElement(required = true, nillable = false)
private String first;
@XmlElement(required = true, nillable = false)
private String last;
@XmlElementWrapper(name = "contacts")
@XmlElementRefs({
@XmlElementRef(name = "phone", type = Phone.class, required = true),
@XmlElementRef(name = "address", type = Address.class, required = true)
})
private final List<ContactInfomation> contacts = new ArrayList<>();
}
ContactInformation
is used only as container:
public abstract class ContactInfomation { /* empty class */ }
Phone
class:
@XmlRootElement
@XmlAccessorType(XmlAccessType.NONE)
public class Phone extends ContactInfomation {
@XmlElement(required = true, nillable = false)
private String number;
}
And Address
class:
@XmlRootElement
@XmlAccessorType(XmlAccessType.NONE)
public class Address extends ContactInfomation {
@XmlElement(required = true, nillable = false)
private String country;
@XmlElement(required = true, nillable = false)
private String city;
}
The problem is when I change @XmlElementrefs
to @XmlElements
in Person
class, nothing happens. JAXB
maps them to xs:choice
. The XML output is same as before and schemagen
generates the same schema as before.
Here is a sample output:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<person>
<first>majid</first>
<last>azimi</last>
<contacts>
<address>
<country>US</country>
<city>Los Angles</city>
</address>
<address>
<country>US</country>
<city>New York</city>
</address>
<phone>
<number>5551037</number>
</phone>
</contacts>
</person>
And here is the schema:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<xs:schema version="1.0" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="address" type="address"/>
<xs:element name="person" type="person"/>
<xs:element name="phone" type="phone"/>
<xs:complexType name="person">
<xs:sequence>
<xs:element name="first" type="xs:string"/>
<xs:element name="last" type="xs:string"/>
<xs:element name="contacts" minOccurs="0">
<xs:complexType>
<xs:sequence>
<xs:choice minOccurs="0" maxOccurs="unbounded">
<xs:element ref="phone"/>
<xs:element ref="address"/>
</xs:choice>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
<xs:complexType name="phone">
<xs:complexContent>
<xs:extension base="contactInfomation">
<xs:sequence>
<xs:element name="number" type="xs:string"/>
</xs:sequence>
</xs:extension>
</xs:complexContent>
</xs:complexType>
<xs:complexType name="contactInfomation" abstract="true">
<xs:sequence/>
</xs:complexType>
<xs:complexType name="address">
<xs:complexContent>
<xs:extension base="contactInfomation">
<xs:sequence>
<xs:element name="country" type="xs:string"/>
<xs:element name="city" type="xs:string"/>
</xs:sequence>
</xs:extension>
</xs:complexContent>
</xs:complexType>
</xs:schema>
Documentation of @XmlElementRef
uses it for Substitution group and XML choice. I'm completely confused what is the difference of @XmlElementRefs
/@XmlElementRef
and @XmlElements
/@XmlElement
. Can anyone help?
Annotation Type XmlElementRef. @Retention(value=RUNTIME) @Target(value={FIELD,METHOD}) public @interface XmlElementRef. Maps a JavaBean property to a XML element derived from property's type. Usage. @XmlElementRef annotation can be used with a JavaBean property or from within XmlElementRefs.
@XmlRootElement annotation can be used to map a class or enum type to XML type. When a top level class or an enum type is annotated with the @XmlRootElement annotation, then its value is represented as XML element in an XML document.
Annotation Type XmlRootElementMaps a class or an enum type to an XML element. Usage. The @XmlRootElement annotation can be used with the following program elements: a top level class. an enum type.
The JAXB annotations defined in the javax. xml. bind. annotations package can be used to customize Java program elements to XML schema mapping.
Basic Differenece Between @XmlElement
and @XmlElementRef
The difference between @XmlElement
and @XmlElementRef
is if the corresponding generated element will contain a local element definition or a reference to a global element definition.
Choice and Substitution Groups
Choice in XML Schema is really a superset of what can be done with substitution groups. So to simplifiy the mapping JAXB leverages one mapping for both.
JAXB and Schema Generation
JAXB can generated a Java model from any XML Schema, on the flip side JAXB does not preserve all metadata about the XML schema. Therefore JAXB can't generate every XML schema.
@XmlElementRefs
/@XmlElementRef
and @XmlRootElement
Below is what you have in your model.
Person
I have modified the mapping on the contacts
field to make the mapping to the Address
class more distinct.
@XmlElementWrapper(name = "contacts")
@XmlElementRefs({
@XmlElementRef(name = "phone", type = Phone.class, required = true),
@XmlElementRef(name = "ADDRESS", type = Address.class, required = true)
})
private final List<ContactInfomation> contacts = new ArrayList<>();
Phone
When you map with @XmlElementRef
the information you specify needs to correspond to a global element definition supplied by @XmlRootElement
or @XmlElementDecl
(see: http://blog.bdoughan.com/2012/07/jaxb-and-root-elements.html). By default the root element for the Person
class with be person
.
@XmlRootElement
public class Person {
Address
I have used the @XmlRootElement
annotation to override the default name for the Address class
.
@XmlRootElement(name="ADDRESS")
public class Address extends ContactInfomation {
Generated XML Schema
Here is the generated schema. We see that the element
definitions within the choice
structure leverage ref
to reference an existing element instead of defining a local element.
<xs:element name="contacts" minOccurs="0">
<xs:complexType>
<xs:sequence>
<xs:choice minOccurs="0" maxOccurs="unbounded">
<xs:element ref="phone"/>
<xs:element ref="ADDRESS"/>
</xs:choice>
</xs:sequence>
</xs:complexType>
</xs:element>
@XmlElements
/@XmlElement
I have changed the mapping on your contacts
field to use @XmlElements
.
Person
@XmlElementWrapper(name = "contacts")
@XmlElements({
@XmlElement(name = "phone-number", type = Phone.class, required = true),
@XmlElement(name = "home-address", type = Address.class, required = true)
})
private final List<ContactInfomation> contacts = new ArrayList<>();
Phone
None of the referenced classes are required to be annotated with @XmlRootElement
(or have a corresponding @XmlElementDecl
annotation.
public class Phone extends ContactInfomation {
Address
If he class does have an @XmlRootElement
annotation it is not required to match the @XmlElement
annotation within the @XmlElements
.
@XmlRootElement(name="ADDRESS")
public class Address extends ContactInfomation {
Generated XML Schema
Here is the generated schema. We see that the element
definitions within the choice
structure are now defined as local elements.
<xs:element name="contacts" minOccurs="0">
<xs:complexType>
<xs:sequence>
<xs:choice maxOccurs="unbounded">
<xs:element name="phone-number" type="phone"/>
<xs:element name="home-address" type="address"/>
</xs:choice>
</xs:sequence>
</xs:complexType>
</xs:element>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With