Search code examples
javamavenxsdmaven-pluginjaxb2-maven-plugin

SAXParseException: '<element>' is already defined: how to generate Java code using JAXB2 Maven Plugin from conflicting XSD definitions?


I have two XSD documents over which I have no control - i.e. I cannot force their authors to change them. Unfortunately, the XSDs are not valid when used side by side, because they contain conflicting definitions:

<!-- first.xsd -->
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:onl="http://example/data/online" xmlns:xs="http://www.w3.org/2001/XMLSchema"  targetNamespace="http://example/data/online" elementFormDefault="qualified" attributeFormDefault="unqualified" version="2.4">
    <xs:element name="content">
        <xs:complexType>
            <xs:sequence>
                <xs:element name="docSeqId" type="xs:long">
                    <xs:annotation>
                        <xs:documentation>Identifier of a documentation sequence.</xs:documentation>
                    </xs:annotation>
                </xs:element>
                <xs:element name="customerName" type="xs:string">
                    <xs:annotation>
                        <xs:documentation>Name of a customer.</xs:documentation>
                    </xs:annotation>
                </xs:element>
            </xs:sequence>
        </xs:complexType>
    </xs:element>
</xs:schema>

<!-- second.xsd -->
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:onl="http://example/data/online" xmlns:xs="http://www.w3.org/2001/XMLSchema"  targetNamespace="http://example/data/online" elementFormDefault="qualified" attributeFormDefault="unqualified" version="2.4">
    <xs:element name="content">
        <xs:complexType>
            <xs:sequence>
                <xs:element name="transactionId" type="xs:long">
                    <xs:annotation>
                        <xs:documentation>Id of a transaction.</xs:documentation>
                    </xs:annotation>
                </xs:element>
                <xs:element name="amount" type="xs:decimal">
                    <xs:annotation>
                        <xs:documentation>Transaction amount.</xs:documentation>
                    </xs:annotation>
                </xs:element>
                <xs:element name="targetAccountId" type="xs:string">
                    <xs:annotation>
                        <xs:documentation>Id (GUID) of a target account.</xs:documentation>
                    </xs:annotation>
                </xs:element>
            </xs:sequence>
        </xs:complexType>
    </xs:element>
</xs:schema>

I created the following jaxb2-maven-plugin configuration:

<plugin>
    <groupId>org.codehaus.mojo</groupId>
    <artifactId>jaxb2-maven-plugin</artifactId>
    <version>3.1.0</version>
    <configuration>
        <outputDirectory>${project.build.directory}/generated-sources/</outputDirectory>
    </configuration>
    <executions>
        <execution>
            <id>generate-source-code</id>
            <goals>
                <goal>xjc</goal>
            </goals>
            <configuration>
                <sources>
                    <source>src/main/resources/integration/first.xsd</source>
                    <source>src/main/resources/integration/second.xsd</source>
                </sources>
            </configuration>
        </execution>
    </executions>
</plugin>

but when I execute relevant maven phase, the build exits with the following error:

[ERROR] file:/C:/Users/Andy/development/sample-project/src/main/resources/integration/second.xsd [23,18] org.xml.sax.SAXParseException: 'content' is already defined

How can I generate valid Java code from the XSD files, so that I can use them?


Solution

  • I figured out I was facing two issues:

    1. generation would lead to same classes, i.e. I would have duplicate definition of Content class in a same package - which obviously does not work,
    2. conflicting XSD files, which would not allow jaxb2-maven-plugin to process them simultaneously.

    In order to solve the first of the two problems, I use two .xjb (XML-to-Java binding file, one for each of the invalid .xsds), where I defined in which Java package source code from a given .xsd should be placed.

    The binding files look like this:

    <!-- first.xjb -->
    <jxb:bindings version="3.0"
                  xmlns:jxb="https://jakarta.ee/xml/ns/jaxb"
                  xmlns:xsd="http://www.w3.org/2001/XMLSchema">
        <jxb:bindings schemaLocation="../integration/first.xsd" node="//xsd:schema">
            <jxb:schemaBindings>
                <jxb:package name="example.data.online.first"/>
            </jxb:schemaBindings>
        </jxb:bindings>
    </jxb:bindings>
    
    
    <!-- second.xjb -->
    <jxb:bindings version="3.0"
                  xmlns:jxb="https://jakarta.ee/xml/ns/jaxb"
                  xmlns:xsd="http://www.w3.org/2001/XMLSchema">
        <jxb:bindings schemaLocation="../integration/second.xsd" node="//xsd:schema">
            <jxb:schemaBindings>
                <jxb:package name="example.data.online.second"/>
            </jxb:schemaBindings>
        </jxb:bindings>
    </jxb:bindings>
    

    Using the binding, (among other things), I was able to override the target Java package.

    I solved the second problem by creating two separate executions of the jaxb2-maven-plugin, one for each of the conflicting .xsds, and each of them using their relevant .jxb binding file.

    Important: By default, jaxb2-maven-plugin clears the output directory before each generation execution. In my case I want to merge generated code together, this can be done by setting the clearOutputDir argument to false.

    The final configuration of the plugin looks like this:

    <plugin>
        <groupId>org.codehaus.mojo</groupId>
        <artifactId>jaxb2-maven-plugin</artifactId>
        <version>3.1.0</version>
        <configuration>
            <outputDirectory>${project.build.directory}/generated-sources/</outputDirectory>
            <clearOutputDir>false</clearOutputDir>
        </configuration>
        <executions>
            <execution>
                <id>first-generator</id>
                <goals>
                    <goal>xjc</goal>
                </goals>
                <configuration>
                    <sources>
                        <source>src/main/resources/integration/first.xsd</source>
                    </sources>
                    <xjbSources>
                        <xjbSource>src/main/resources/jaxb2/first.xjb</xjbSource>
                    </xjbSources>
                </configuration>
            </execution>
            <execution>
                <id>second-generator</id>
                <goals>
                    <goal>xjc</goal>
                </goals>
                <configuration>
                    <sources>
                        <source>src/main/resources/integration/second.xsd</source>
                    </sources>
                    <xjbSources>
                        <xjbSource>src/main/resources/jaxb2/second.xjb</xjbSource>
                    </xjbSources>
                </configuration>
            </execution>
        </executions>
    </plugin>
    

    leading to correct Java code generation:

    generated Java beans from XSD