Today I answered a question on Stack Overflow (feel free to up vote) about JAXB and generating Java enums from an XML Schema. This is normally straight forward, but there are a couple "gotchas" to be aware of. In this post I'll demonstrate an easy and a harder use case.
XML Schema (enums.xsd)
The following XML schema will be used for this example. Note how it contains two enumerations: status-type and education-level-type.
<?xml version="1.0" encoding="UTF-8"?> <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns="http://www.example.org/enum" targetNamespace="http://www.example.org/enum" elementFormDefault="qualified"> <xs:element name="person"> <xs:complexType> <xs:sequence> <xs:element name="membership" type="status-type" /> <xs:element name="education-level" type="education-level-type" /> </xs:sequence> </xs:complexType> </xs:element> <xs:simpleType name="status-type"> <xs:restriction base="xs:string"> <xs:enumeration value="active"/> <xs:enumeration value="inactive"/> </xs:restriction> </xs:simpleType> <xs:simpleType name="education-level-type"> <xs:restriction base="xs:string"> <xs:enumeration value="1-6"/> <xs:enumeration value="6-12"/> <xs:enumeration value="post secondary"/> <xs:enumeration value="college"/> <xs:enumeration value=""/> </xs:restriction> </xs:simpleType> </xs:schema>
Generated Model
XJC Call
We will use JAXB to generate an object model from the XML schema. The following command was used:
xjc -d out enums.xsd
Person
Below is one of the generated classes. Note that an enum was generated for status-type but not for education-level-type.
package org.example._enum; import javax.xml.bind.annotation.XmlAccessType; import javax.xml.bind.annotation.XmlAccessorType; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; import javax.xml.bind.annotation.XmlType; @XmlAccessorType(XmlAccessType.FIELD) @XmlType(name = "", propOrder = { "membership", "educationLevel" }) @XmlRootElement(name = "person") public class Person { @XmlElement(required = true) protected StatusType membership; @XmlElement(name = "education-level", required = true) protected String educationLevel; public StatusType getMembership() { return membership; } public void setMembership(StatusType value) { this.membership = value; } public String getEducationLevel() { return educationLevel; } public void setEducationLevel(String value) { this.educationLevel = value; } }
What Happened?
JAXB did not know how to convert the some of the XML enumeration values to Java enum values so it made the field correspond to the base type of the enumeration. This can happen if the enumeration value starts with a number or is the empty String.
<xs:simpleType name="education-level-type"> <xs:restriction base="xs:string"> <xs:enumeration value="1-6"/> <xs:enumeration value="6-12"/> <xs:enumeration value="post secondary"/> <xs:enumeration value="college"/> <xs:enumeration value=""/> </xs:restriction> </xs:simpleType>
What Can be Done?
We can have JAXB generate a Java enum for the XML enumeration education-level-type, by providing a schema bindings file to supply valid enum values.
bindings.xml
<jxb:bindings xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:jxb="http://java.sun.com/xml/ns/jaxb" version="2.1"> <jxb:bindings schemaLocation="enums.xsd"> <jxb:bindings node="//xs:simpleType[@name='education-level-type']/xs:restriction/xs:enumeration[@value='1-6']"> <jxb:typesafeEnumMember name="ONE_TO_SIX"/> </jxb:bindings> <jxb:bindings node="//xs:simpleType[@name='education-level-type']/xs:restriction/xs:enumeration[@value='6-12']"> <jxb:typesafeEnumMember name="SIX_TO_TWELVE"/> </jxb:bindings> <jxb:bindings node="//xs:simpleType[@name='education-level-type']/xs:restriction/xs:enumeration[@value='']"> <jxb:typesafeEnumMember name="BLANK"/> </jxb:bindings> </jxb:bindings> </jxb:bindings>
XJC Call
The schema bindings file is included in the XJC call as follows:
xjc -d out -b bindings.xml enums.xsd
Person
Now the field corresponding to the XML enumeration education-level-type corresponds to the Java enum EducationLevelType.
package org.example._enum; import javax.xml.bind.annotation.XmlAccessType; import javax.xml.bind.annotation.XmlAccessorType; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; import javax.xml.bind.annotation.XmlType; @XmlAccessorType(XmlAccessType.FIELD) @XmlType(name = "", propOrder = { "membership", "educationLevel" }) @XmlRootElement(name = "person") public class Person { @XmlElement(required = true) protected StatusType membership; @XmlElement(name = "education-level", required = true) protected EducationLevelType educationLevel; public StatusType getMembership() { return membership; } public void setMembership(StatusType value) { this.membership = value; } public EducationLevelType getEducationLevel() { return educationLevel; } public void setEducationLevel(EducationLevelType value) { this.educationLevel = value; } }
EducationLevelType
package org.example._enum; import javax.xml.bind.annotation.XmlEnum; import javax.xml.bind.annotation.XmlEnumValue; import javax.xml.bind.annotation.XmlType; @XmlType(name = "education-level-type") @XmlEnum public enum EducationLevelType { @XmlEnumValue("1-6") ONE_TO_SIX("1-6"), @XmlEnumValue("6-12") SIX_TO_TWELVE("6-12"), @XmlEnumValue("post secondary") POST_SECONDARY("post secondary"), @XmlEnumValue("college") COLLEGE("college"), @XmlEnumValue("") BLANK(""); private final String value; EducationLevelType(String v) { value = v; } public String value() { return value; } public static EducationLevelType fromValue(String v) { for (EducationLevelType c: EducationLevelType.values()) { if (c.value.equals(v)) { return c; } } throw new IllegalArgumentException(v); } }
Further Reading
If you enjoyed this post, then you may also be interested in:
To solve the numeric enumeration values (eg: 1001, 1002 etc) used in the XSD, I used
ReplyDelete<globalBindings typesafeEnumMemberName="generateName" />
In my bindings file (.xjb) and the generated enum constants came out VALUE_1001 , VALUE_1002, etc
This comment has been removed by a blog administrator.
ReplyDeleteHi Blaise,
ReplyDeleteI have a problem with Eclipse and conversion from xsd schema to java class.
I've used your example below:
The problem is, if I have only number in value, for example:
The Enum class is not being generated. If I delete rows:
than everything generates OK. Do you have any suggestion?
Thank you in advance,
Tomy
Your XML schema didn't survive the comment section, but the same bindings file approach will work for use case. Is this your Stack Overflow question:
Delete- http://stackoverflow.com/questions/18617825/converting-xsd-enum-to-java-class-in-eclipse