Reputation: 15008
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
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