daniu
daniu

Reputation: 15008

JAXB Overwriting package-info.java: What should be "namespace"?

We're using xjc to generate JAXB Java classes for XML generation. Everything works fine except we tried to adjust the generated namespace prefixes as described here. We're stuck with "solution 2", adjusting package-info.java, due to the JAXB version we're using.

The structure we have is several imports deep: root namespace imports other namespace, which in turn imports yet a third one.

MCVE xsds

root.xsd (imports other.xsd):

<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns="root" xmlns:other="other" targetNamespace="root" version="1.0">
    <xs:import namespace="other" schemaLocation="other.xsd" />
    <xs:element name="rootElem">
        <xs:complexType>
            <xs:choice>
                <xs:element ref="rootElem1"/>
            </xs:choice>
        </xs:complexType>
    </xs:element>
    <xs:element name="rootElem1" nillable="false">
        <xs:complexType>
            <xs:sequence>
                <xs:element name="data">
                    <xs:complexType>
                        <xs:choice>
                            <xs:element ref="other:otherElem"/>
                        </xs:choice>
                    </xs:complexType>
                </xs:element>
            </xs:sequence>
        </xs:complexType>
    </xs:element>
    </xs:schema>

other.xsd (imports third.xsd):

<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:other="other" xmlns:third="third" targetNamespace="other" elementFormDefault="qualified" attributeFormDefault="unqualified" version="1.0">
<xsd:import namespace="third" schemaLocation="third.xsd" />
    <xsd:element name="otherElem">
        <xsd:complexType>
            <xsd:sequence>
                <xsd:element ref="third:thirdElem" />
            </xsd:sequence>
        </xsd:complexType>
    </xsd:element>
</xsd:schema>

third.xsd:

<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:third="third" targetNamespace="third" elementFormDefault="qualified" attributeFormDefault="unqualified" version="1.0">
    <xsd:element name="thirdElem">
        <xsd:complexType>
            <xsd:sequence>
                <xsd:element name="thirdData" type="xsd:string" />
            </xsd:sequence>
        </xsd:complexType>
    </xsd:element>
</xsd:schema>

With this

Simple test case

@Test
public void test() throws JAXBException
{
    Marshaller marshaller = JAXBContext.newInstance("test.jaxb.generated").createMarshaller();
    marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
    marshaller.setProperty(Marshaller.JAXB_ENCODING, "UTF-8");
    marshaller.setProperty(Marshaller.JAXB_SCHEMA_LOCATION, "http://www.example.com/schema.xsd");

    RootElem root = new RootElem();
    RootElem1 root1 = new RootElem1();
    Data d = new Data();
    OtherElem other = new OtherElem();
    ThirdElem thirdElem = new ThirdElem();
    thirdElem.setThirdData("third");
    other.setThirdElem(thirdElem);
    d.setOtherElem(other);
    root1.setData(d);
    root.setRootElem1(root1);

    Path path = Paths.get("target", "outfile.xml");

    Result result = new StreamResult(path.toFile());
    marshaller.marshal(root, result);
}

this results in this

Generated XML

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<ns4:rootElem xmlns:ns2="other" xmlns:ns3="third" xmlns:ns4="root" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.example.com/schema.xsd">
    <ns4:rootElem1>
        <data>
            <ns2:otherElem>
                <ns3:thirdElem>
                    <ns3:thirdData>third</ns3:thirdData>
                </ns3:thirdElem>
            </ns2:otherElem>
        </data>
    </ns4:rootElem1>
</ns4:rootElem>

and everything is fine (except data which doesn't have any namespace associated, I'm assuming because that's an inner type).

Now this is partly the question: here's the

generated package-info.java

@javax.xml.bind.annotation.XmlSchema(namespace = "other", elementFormDefault = javax.xml.bind.annotation.XmlNsForm.QUALIFIED)
package test.jaxb.generated;

Why is the namespace given referring to other? My root namespace is root. (root.xsd is the only file we're giving our Maven jaxb2-maven-plugin; we can include the others, it makes no difference).

Wrong replacement package-info.java

If we overwrite the generated one with this:

@javax.xml.bind.annotation.XmlSchema(
        namespace = "root",
        xmlns = {
                @javax.xml.bind.annotation.XmlNs(prefix = "t", namespaceURI = "third"),
                @javax.xml.bind.annotation.XmlNs(prefix = "o", namespaceURI = "other"),
                @javax.xml.bind.annotation.XmlNs(prefix = "r", namespaceURI = "root")
        },
        elementFormDefault = javax.xml.bind.annotation.XmlNsForm.QUALIFIED)
package test.jaxb.generated;

which we initially did because we assumed we have to give the root namespace here - this is the

Wrong Generated XML

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<r:rootElem xmlns:t="third" xmlns:o="other" xmlns:r="root" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.example.com/schema.xsd">
    <r:rootElem1>
        <data>
            <r:otherElem>
                <t:thirdElem>
                    <t:thirdData>third</t:thirdData>
                </t:thirdElem>
            </r:otherElem>
        </data>
    </r:rootElem1>
</r:rootElem>

Now the namespaces are pretty, but wrong! otherElem belongs to o, not r.

Changing the namespace in the overwriting file back to other fixes the error, but again:

the question is why is the required namespace other here? Just as confusing is the fact that the third imported layer is correct either way.

The problem is fixed, but we'd like to understand the concept. What are we missing?

EDIT:

For completeness' sake, here's the build section of my pom.xml:

<build>
    <plugins>
        <plugin>
        <!-- run xjc -->
            <groupId>org.codehaus.mojo</groupId>
            <artifactId>jaxb2-maven-plugin</artifactId>
            <executions>
                <execution>
                    <id>xjc-generate_classes</id>
                    <goals>
                        <goal>xjc</goal>
                    </goals>
                    <configuration>
                        <packageName>test.jaxb.generated</packageName>
                        <schemaFiles>
                            root.xsd
                        </schemaFiles>
                        <schemaDirectory>${basedir}/src/main/resources/schemata</schemaDirectory>
                        <extension>true</extension>
                        <bindingDirectory>${basedir}/src/main/resources/bindings</bindingDirectory>
                        <outputDirectory>${basedir}/target/generated-sources/jaxb/</outputDirectory>
                    </configuration>
                </execution>
            </executions>
        </plugin>
        <!-- overwrite created package-info.java -->
         <plugin>
            <artifactId>maven-resources-plugin</artifactId>
            <executions>
                <execution>
                    <id>copy-resources</id>
                    <phase>process-resources</phase>
                    <goals>
                        <goal>copy-resources</goal>
                    </goals>
                    <configuration>
                        <overwrite>true</overwrite>
                        <outputDirectory>${basedir}/target/generated-sources/jaxb/test/jaxb/generated</outputDirectory>
                        <resources>
                            <resource>
                                <directory>${basedir}/src/main/resources/bindings</directory>
                                <include>package-info.java</include>
                            </resource>
                        </resources>
                    </configuration>
                </execution>
            </executions>
        </plugin>
    </plugins>
</build>

Upvotes: 2

Views: 11258

Answers (1)

lexicore
lexicore

Reputation: 43689

The problem is that you generate all your classes in one package (configured using the packageName of the Maven plugin.

Do not do this.

JAXB is somewhat based on the concept of package-namespace correspondence. There should be one package per namespace you use. While it is technically possible to do it otherwise, you'll be facing one problem after another. So it is better to follow this concept and use or generate one package per namespace. You can still configure target packages - but using the binding files instead of plugin configuration elements.

Upvotes: 5

Related Questions