Search code examples
javaxsdjaxbxjc

JAXB is not generating enum classes from XSD simple types


I'm trying to migrate to a newer version of an API we use and therefore had to exchange some XSDs. The main difference is that most simple types are now encapsulated within xsd:elements.

Now, with the change, my JAXB XSD to Java class generator is somehow not creating classes from simple types containing enums like it did before. I'll give a full before/after example to clear out misunderstandings:

Before, we had a normal xsd:simpleType:

<xsd:simpleType name="PriorityEnum" final="restriction">
    <xsd:restriction base="xsd:string">
        <xsd:enumeration value="NORMAL" />
        <xsd:enumeration value="LOW" />
        <xsd:enumeration value="HIGH" />
    </xsd:restriction>
</xsd:simpleType>

... which was then referenced by type:

<xsd:complexType name="SecurityType">
    <xsd:sequence>
        ...
        <xsd:element name="PriorityEnum" type="dt:PriorityEnum" minOccurs="0" />
        ...
    </xsd:sequence>
</xsd:complexType>

The generated output had the class SecurityType with a field of enum PriorityEnum.

Now, for the newer XSD, they put the xsd:simpleType into a xsd:element:

<xsd:element name="PriorityEnum">
    <xsd:simpleType final="restriction">
        <xsd:restriction base="xsd:string">
            <xsd:enumeration value="NORMAL" />
            <xsd:enumeration value="LOW" />
            <xsd:enumeration value="HIGH" />
        </xsd:restriction>
    </xsd:simpleType>
</xsd:element>

... and reference it with ref:

<xsd:complexType name="SecurityType">
    <xsd:sequence>
        ...
        <xsd:element ref="dt:PriorityEnum" minOccurs="0" />
        ...
    </xsd:sequence>
</xsd:complexType>

The generated output still has the class SecurityType, but the field is now of type String...

My build.gradle contains the following task for generating classes:

def myXsd = "myxsd-v5.xsd"
def myOutput = "$buildDir/generated/jaxb/model/src/main/java"

tasks.register('generateSources') {
    doLast {
        def jaxbTargetDir = file("$myOutput")

        if (!jaxbTargetDir.exists()) {
            jaxbTargetDir.mkdirs()
        }

        ant.taskdef(name: 'xjc', classname: 'com.sun.tools.xjc.XJCTask', classpath: configurations.jaxb.asPath)

        ant.xjc(
                destdir: "${jaxbTargetDir}",
                schema: "${rootDir}/src/main/resources/xsd/${myXsd}",
                removeOldOutput: 'yes', extension: 'true'
        ) {
            arg(line: '-nv -disableXmlSecurity')
        }
    }
}

tasks.compileJava.dependsOn tasks.openApiGenerate, tasks.generateSources

I'm using the newest version for all JAXB dependencies, 2.3.1. Is this maybe an issue with xjc?


Solution

  • I resolved this with a bindings.xjb:

    <?xml version="1.0" encoding="UTF-8"?>
    <jxb:bindings xmlns:xs="http://www.w3.org/2001/XMLSchema"
                  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                  xmlns:jxb="http://java.sun.com/xml/ns/jaxb"
                  version="2.1">
    
        <jxb:bindings schemaLocation="apiXsd.xsd" node="/xs:schema">
            <jxb:bindings node="//xs:element[@name='MyEnum']//xs:simpleType">
                <jxb:typesafeEnumClass name="MyEnum" />
            </jxb:bindings>
            <jxb:bindings node="//xs:element[@name='AnotherEnum']//xs:simpleType">
                <jxb:typesafeEnumClass name="AnotherEnum" />
            </jxb:bindings>
            <jxb:bindings node="//xs:element[@name='ThirdEnum']//xs:simpleType">
                <jxb:typesafeEnumClass name="ThirdEnum" />
            </jxb:bindings>
        </jxb:bindings>
    
    </jxb:bindings>
    

    This needs one entry per class to generate, but it's better than manually adjusting the XSD whenever the API partner releases a new version with a new XSD.

    The bindings.xjb is included in the ant.xjc:

    ant.xjc(
            destdir: "${targetDir}",
            schema: "${xsdDir}/${xsdName}",
            removeOldOutput: 'yes', extension: 'true'
    ) {
        arg(line: '-nv -disableXmlSecurity')
        binding(dir: "${xsdDir}", includes: 'bindings.xjb')
    }