javaxsdjaxbmaven-jaxb2-pluginjaxb2-maven-plugin

How to generate java classes from multiple xsd with common schema using maven


I'm trying to generate a java classes from multiple xsd files. But I get this error.

org.xml.sax.SAXParseException; ... 'somelement' is already defined

I think it's due to the common types that included multiple times.

How can I generate the java classes with maven?

I have the following schemas in a directory:

xsd:

EXAMPLE_QualifyRS.xsd

<xs:schema xmlns="http://www.example.org/EXAMPLE/2007/00" xmlns:xs="http://www.w3.org/2001/XMLSchema" targetNamespace="http://www.example.org/EXAMPLE/2007/00" elementFormDefault="qualified" version="1.002" id="EXAMPLE2016.1">
    <xs:include schemaLocation="EXAMPLE_CommonTypes.xsd"/>
    <xs:include schemaLocation="EXAMPLE_SimpleTypes.xsd"/>
    <xs:include schemaLocation="EXAMPLE_Peble_CommonTypes.xsd"/>
         <xs:element name="someelement">...</xs:element>

EXAMPLE_Peble_CommonTypes.xsd

<xs:schema xmlns="http://www.example.org/EXAMPLE/2007/00" xmlns:xs="http://www.w3.org/2001/XMLSchema" targetNamespace="http://www.example.org/EXAMPLE/2007/00" elementFormDefault="qualified" version="1.000" id="EXAMPLE2016.1">
    <xs:include schemaLocation="EXAMPLE_CommonTypes.xsd"/>

EXAMPLE_QualifyRQ.xsd

<xs:schema xmlns="http://www.example.org/EXAMPLE/2007/00" xmlns:xs="http://www.w3.org/2001/XMLSchema" targetNamespace="http://www.example.org/EXAMPLE/2007/00" elementFormDefault="qualified" version="1.001" id="EXAMPLE2016.1">
    <xs:include schemaLocation="EXAMPLE_CommonTypes.xsd"/>
    <xs:include schemaLocation="EXAMPLE_SimpleTypes.xsd"/>
    <xs:include schemaLocation="EXAMPLE_Peble_CommonTypes.xsd"/>
        <xs:element name="someelement">...</xs:element>

And here is how I generate my class in maven

<plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>jaxb2-maven-plugin</artifactId>
                <version>2.4</version>
                <executions>
                    <execution>
                        <id>example-schema</id>
                        <phase>generate-sources</phase>
                        <goals>
                            <goal>xjc</goal>
                        </goals>
                        <configuration>
                            <xsdPathWithinArtifact>xsd</xsdPathWithinArtifact>
                            <addGeneratedAnnotation>true</addGeneratedAnnotation>
                            <laxSchemaValidation>true</laxSchemaValidation>
                            <laxSchemaValidation>true</laxSchemaValidation>
                            <readOnly>true</readOnly>
                            <verbose>true</verbose>
                            <sources>
                                <source>src/main/resources/xsd</source>
                            </sources>
                            <packageName>com.example</packageName>
                        </configuration>
                    </execution>
                </executions>
            </plugin>

Solution

  • Let's say you have a "Person" in your common.xsd, as example below:

    <xs:schema xmlns="common.schema.def"
               xmlns:xs="http://www.w3.org/2001/XMLSchema"
               targetNamespace="common.schema.def"
               elementFormDefault="qualified">
    
        <xs:complexType name="Person">
            <xs:sequence>
                    <xs:element name="name" type="xs:string"/>
                    <xs:element name="age" type="xs:int"/>
            </xs:sequence>
        </xs:complexType>
    </xs:schema>
    

    And you want to use it in another schema. Then you would do:

    <xs:schema xmlns="http://www.example.org/EXAMPLE/2007/00"
               xmlns:xs="http://www.w3.org/2001/XMLSchema"
               xmlns:common="common.schema.def"
               targetNamespace="http://www.example.org/EXAMPLE/2007/00"
               elementFormDefault="qualified">
        <xs:import namespace="common.schema.def" schemaLocation="common.xsd"/>
    
        <xs:complexType name="someClass">
            <xs:sequence>
                <xs:element name="person" type="common:Person"/>
                <xs:element name="alias" type="xs:string"/>
            </xs:sequence>
        </xs:complexType>
    </xs:schema>
    

    Edit to reply on the comment:

    The name collision happens because you specify package name in the plugin. Then classes with the same name are attempted to be placed under the same package. Let's remove the package naming from the plugin and use bindings to create the desired package per schema.

    Your plugin would look like this:

    <plugin>
        <groupId>org.codehaus.mojo</groupId>
        <artifactId>jaxb2-maven-plugin</artifactId>
        <version>2.4</version>
        <executions>
            <execution>
                <id>example-schema</id>
                <phase>generate-sources</phase>
                <goals>
                    <goal>xjc</goal>
                </goals>
                <configuration>
                    <xsdPathWithinArtifact>xsd</xsdPathWithinArtifact>
                    <addGeneratedAnnotation>true</addGeneratedAnnotation>
                    <laxSchemaValidation>true</laxSchemaValidation>
                    <laxSchemaValidation>true</laxSchemaValidation>
                    <readOnly>true</readOnly>
                    <verbose>true</verbose>
                    <sources>
                        <source>src/main/resources/xsd</source>
                    </sources>
                    <xjbSources>
                        <xjbSource>src/main/resources/xjb</xjbSource>
                    </xjbSources>
                </configuration>
            </execution>
        </executions>
    </plugin>
    

    And the bindings.xjb would look like this:

    <jxb:bindings version="1.0"
                  xmlns:jxb="http://java.sun.com/xml/ns/jaxb"
                  xmlns:xsd="http://www.w3.org/2001/XMLSchema">
        <jxb:bindings schemaLocation="../xsd/common.xsd">
            <jxb:schemaBindings>
                <jxb:package name="common.packagename"/>
            </jxb:schemaBindings>
        </jxb:bindings>
        <jxb:bindings schemaLocation="../xsd/someothercommon.xsd">
            <jxb:schemaBindings>
                <jxb:package name="othercommon.packagename"/>
            </jxb:schemaBindings>
        </jxb:bindings>
    </jxb:bindings>
    

    To test it I used another xsd with an element named Person that looks like this:

    <xs:schema xmlns="othercommon.schema.def"
               xmlns:xs="http://www.w3.org/2001/XMLSchema"
               targetNamespace="othercommon.schema.def"
               elementFormDefault="qualified">
    
        <xs:complexType name="Person">
            <xs:sequence>
                <xs:element name="alias" type="xs:string"/>
                <xs:element name="id" type="xs:int"/>
            </xs:sequence>
        </xs:complexType>
    </xs:schema>
    

    And the schema that uses both is updated to this:

    <xs:schema xmlns="http://www.example.org/EXAMPLE/2007/00"
               xmlns:xs="http://www.w3.org/2001/XMLSchema"
               xmlns:common="common.schema.def"
               xmlns:othercommon="othercommon.schema.def"
               targetNamespace="http://www.example.org/EXAMPLE/2007/00"
               elementFormDefault="qualified">
        <xs:import namespace="common.schema.def" schemaLocation="common.xsd"/>
        <xs:import namespace="othercommon.schema.def" schemaLocation="someothercommon.xsd"/>
    
        <xs:complexType name="someClass">
            <xs:sequence>
                <xs:element name="person" type="common:Person"/>
                <xs:element name="otherpersontype" type="othercommon:Person"/>
            </xs:sequence>
        </xs:complexType>
    </xs:schema>