jaxb2-maven-plugin generating package-info.java with xmlns prefixes - java

I want to generate java classes with the jaxb2-maven-plugin. I am using the following configuration:
pom.xml:
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>jaxb2-maven-plugin</artifactId>
<version>2.5.0</version>
<executions>
<execution>
<id>SomeID</id>
<goals>
<goal>xjc</goal>
</goals>
<configuration>
<extension>true</extension>
<clearOutputDir>true</clearOutputDir>
<sources>
<source>src/main/xsd/schema.xsd</source>
</sources>
<noGeneratedHeaderComments>true</noGeneratedHeaderComments>
</configuration>
</execution>
</executions>
</plugin>
schema.xsd:
<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema targetNamespace="http://my.target.namespace/uri"
xmlns="http://my.target.namespace/uri"
elementFormDefault="qualified"
attributeFormDefault="unqualified"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:h="http://my.uri.for.prefix.h"
xmlns:f="http://my.target.namespace/uri">
<xsd:import namespace="http://my.uri.for.prefix.h" schemaLocation="schema2.xsd"/>
<xsd:complexType name="FooType">
<xsd:sequence>
<xsd:element ref="h:something" minOccurs="0" maxOccurs="1"/>
</xsd:sequence>
</xsd:complexType>
<xsd:element name="FooType" type="FooType" />
</xsd:schema>
The Jaxb2 plugin is generating me the following package-info.java:
#javax.xml.bind.annotation.XmlSchema(namespace = "http://my.target.namespace/uri", elementFormDefault = javax.xml.bind.annotation.XmlNsForm.QUALIFIED)
package ...;
But, what I want to get is this:
#javax.xml.bind.annotation.XmlSchema(namespace = "http://my.target.namespace/uri", xmlns = {
#XmlNs(prefix="f", namespaceURI="http://my.target.namespace/uri"),
#XmlNs(prefix="h", namespaceURI="http://my.uri.for.prefix.h")
}, elementFormDefault = javax.xml.bind.annotation.XmlNsForm.QUALIFIED)
package ...;
import javax.xml.bind.annotation.XmlNs;
The prefixes are missing in my generated file. How to do that? I tried already to create a binding file but this didn't worked how I expected.

Please see this answer on how to solve this problem:
https://stackoverflow.com/a/10812236/1389219
The answer is very well written and easy to follow. Basically you will have to:
Drop jaxb2-maven-plugin in favour of maven-jaxb2-plugin.
Include the jaxb2-namespace-prefix dependency and provide the <arg>-Xnamespace-prefix</arg>.
Write a new bindings.xml file which is only a few lines long.
Your POM file will become more verbose, but it is worth it to have a package-info.java generated the way you require.
As a bonus, there are a heap of additional plugins and dependencies related to maven-jaxb2-plugin that provide extra features. One that I found helpful was jaxb2-rich-contract-plugin that gave the ability to generate builders and make the generated classes immutable*.
* Well, not strictly speaking immutable (as it just changes the setter methods to be package private), but enough to make them feel safer.

Related

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>
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.

Getting a list of JAXBElement where I want a list of predefined java objects

I use Apache Camel + JAXB for Soap processing. The java glasses are generated by a maven plugin called cxf-codegen-plugin.
The Problem I am facing is that when I want to use a property which is a list. In that case I will always get a list of JAXBElement instead of objects of the correct class.
Assume this given xml snipped:
<domainObjects avqxsi:type="avqsq:AssetAllocation" id="100" name="Some Name">
<nodes>101</nodes>
<nodes>102</nodes>
</domainObjects>
Now all the "nodes" are ids of different domain objects of type AANode. So in the xsd it is defined like so:
<xsd:complexType name="AssetAllocation">
<xsd:complexContent>
<xsd:extension base="avqsq:DomainObject">
<xsd:sequence>
<xsd:element ecore:reference="avqsq:AANode" maxOccurs="unbounded" name="nodes" type="xsd:IDREF"/>
</xsd:sequence>
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
And I have defined some bindings.xml:
<jaxb:bindings node="xsd:complexType[#name='AssetAllocation']//xsd:element[#name='nodes']">
<jaxb:property>
<jaxb:baseType name="my.api.xsd.AANode"/>
</jaxb:property>
</jaxb:bindings>
What I want is a POJO property like this:
#XmlElementRef(name = "nodes")
protected List<AANode> nodes;
But what I actually get at runtime is a List<JAXBElement<AANode>> which leads into a ClassCastException.
EDIT 1:
I have missed the fact that the cxf-codegen framework is generating a class where you clearly can see that the property is annotated with JAXBElement.class which i think is wrong. Interestingly changing the annotation by hand to AANode.class will fail with an IllegalAnnotationException: AANode" or any of its subclasses are not known to this context.
public class AssetAllocation
extends DomainObject
implements Serializable, Equals, HashCode, ToString
{
#XmlElementRef(name = "nodes", type = JAXBElement.class)
protected List<AANode> nodes;
apache CXF code gen plugin will always generate codes with JAXBElement until you set the generate element property flag.
Please create Jaxb binding.xml and refer that binding xml in your code gen plugin section from pom file as below
binding.xml
<?xml version="1.0" encoding="UTF-8"?>
<jaxb:bindings version="2.0"
xmlns:jaxb="http://java.sun.com/xml/ns/jaxb">
<jaxb:bindings>
<jaxb:globalBindings generateElementProperty="false"/>
</jaxb:bindings>
</jaxb:bindings>
code gen plugin
<plugin>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-codegen-plugin</artifactId>
<version>3.1.1</version>
<executions>
<execution>
<id>generate-sources</id>
<phase>generate-sources</phase>
<goals>
<goal>wsdl2java</goal>
</goals>
<configuration>
<wsdlOptions>
<wsdlOption>
<wsdl>${basedir}/src/main/resources/META-INF/wsdl/CxfExampleService.wsdl</wsdl>
<bindingFiles>
<bindingFile>${basedir}/src/main/resources/META-INF/wsdl/binding/bindings.xml</bindingFile>
</bindingFiles>
</wsdlOption>
</wsdlOptions>
</configuration>
</execution>
</executions>
</plugin>
This will resolve the issue
In fact the wsdl2java generates classes with wrong annotations. Instead of
#XmlElementRef(name = "nodes", type = JAXBElement.class)
protected List<AANode> nodes;
One would expect to have:
#XmlIDREF
protected List<AANode> nodes;
I was not able to manage this by bindings.xml. So my final solution is that I use a Byte-Code manipulation to fix the annotations. That way I do not have to mess around with the generated classes or with the generator itself.

Modify java classes to include specific annotations at compile time

I have many classes generated by JAXB's xsd2java. I need all these classes to be annotated with specific annotations at compile time (for example with lombok annotations). Is there any way to do this, with some code generation tool for example?
Disclaimer: I am the author of JAXB2 Annotate Plugin which allows you adding arbitrary annotations to the schema-derived classes.
Short example:
<xsd:complexType name="FooType">
<xsd:annotation>
<xsd:appinfo>
<annox:annotate>#java.lang.SuppressWarnings({"unchecked","rawtypes"})</annox:annotate>
<annox:annotate target="package">#javax.annotation.Generated({"XJC","JAXB2 Annotate Plugin"})</annox:annotate>
</xsd:appinfo>
</xsd:annotation>
<xsd:sequence>
<xsd:element name="bar" type="xsd:string"/>
<xsd:element name="foobar" type="xsd:string">
<xsd:annotation>
<xsd:appinfo>
<annox:annotate>#java.lang.SuppressWarnings({"unchecked","rawtypes"})</annox:annotate>
<annox:annotate target="setter">#java.lang.Deprecated</annox:annotate>
<annox:annotate target="setter-parameter">#java.lang.Deprecated</annox:annotate>
<annox:annotate target="getter">#java.lang.Deprecated</annox:annotate>
<annox:annotate target="field">#java.lang.Deprecated</annox:annotate>
<annox:annotate target="class">#java.lang.Deprecated</annox:annotate>
</xsd:appinfo>
</xsd:annotation>
</xsd:element>
</xsd:sequence>
</xsd:complexType>
Works in external binding files as well.
Limitations:
Annotations are provided in Java syntax, but you have to use fully qualified names.
You have to include annotations classes (lombok, for instance) into the XJC classpath as these classes must be available during the schema compilation time.
ps. I assume that when you said xsd2java you probably meant XJC.
Update
The OP asked about in comments how to configure it with the jaxb2-maven-plugin.
You can use jaxb2-annotate-plugin with jaxb2-maven-plugin as well. I just have never tried it.
You can include additional JARs into classpath by using the dependencies/depenency in pom.xml.
Then you'll need to add arguments into the configuration.
See this answer (and question) for examples:
https://stackoverflow.com/a/12804497/303810
It is about other plugins but you'll get a clue on how to configure it with Codehaus jaxb2-maven-plugin.
Configuration with my maven-jaxb2-plugin would be as follows:
<plugin>
<groupId>org.jvnet.jaxb2.maven2</groupId>
<artifactId>maven-jaxb2-plugin</artifactId>
<configuration>
<extension>true</extension>
<args>
<arg>-Xannotate</arg>
</args>
<plugins>
<plugin>
<groupId>org.jvnet.jaxb2_commons</groupId>
<artifactId>jaxb2-basics-annotate</artifactId>
</plugin>
<plugin>
<groupId>org.jvnet.jaxb2_commons</groupId>
<artifactId>jaxb2-annotate-plugin-test-annox-annotations</artifactId>
</plugin>
</plugins>
</configuration>
</plugin>
This part:
<plugin>
<groupId>org.jvnet.jaxb2_commons</groupId>
<artifactId>jaxb2-annotate-plugin-test-annox-annotations</artifactId>
</plugin>
refers to the artifact containing the annotation classes.
Here's a sample pom.xml for maven-jaxb2-plugin/jaxb2-annotate-plugin combo.

Jaxb doesn't preserve case of namespace in package

I'm using the codehaus jaxb2-maven-plugin, v1.5 to compile XSDs into POJOs, but the generated package name coerces the package name to lower case (so, if I have my target namespace as http://example.com/sampleNamespace, then the generated package is com.example.samplenamespace).
I've googled around a bit and found mostly people having problems with underscores getting munged to dots, and the solution for that, but I can't seem to find something specific for preserving the case of the namespace.
NB: I don't want to have to repeat myself and override the generated package name, so the generatePackage option in the maven config isn't for me.
Before finding about the underscore munging, I had tried that, and also a regular space - both stick a dot in there.
Any ideas?
Schema:
<schema xmlns="http://www.w3.org/2001/XMLSchema" xmlns:jaxb="http://java.sun.com/xml/ns/jaxb"
xmlns:t="http://example.com/sampleNamespace" targetNamespace="http://example.com/sampleNamespace"
jaxb:version="2.0">
<complexType name="MyFirstClass">
<sequence>
<element name="MyFirstElement" type="string" />
</sequence>
</complexType>
</schema>
Maven config:
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>jaxb2-maven-plugin</artifactId>
<version>1.5</version>
<executions>
<execution>
<goals>
<goal>xjc</goal>
</goals>
</execution>
</executions>
</plugin>
You will need to leverage a JAXB bindings file to specify a package name if you do not want to use the one that JAXB generates based on common Java coding conversions.
<bindings
xmlns="http://java.sun.com/xml/ns/jaxb"
version="2.1">
<bindings schemaLocation="schema.xsd">
<schemaBindings>
<package name="com.example.sampleNamespace"/>
</schemaBindings>
</bindings>
</bindings>

Can't use jaxb bindings on individual xsd elements

I'm using jaxb to generate java source code from an xsd file.
I want to be able to specify which packages the sources are generated in on a per element basis, however whenever I generate sources I get the following error:
[ERROR] ****/src/main/xjb/common.xjb[8,24]
com.sun.istack.SAXParseException2: compiler was unable to honor this schemaBinding customization. It is attached to a wrong place, or its inconsistent with other bindings.
My bindings file common.xjb is attempting to place the element with the name (attribute value) 'api' in the package 'com.myxml.common.api':
<jxb:bindings version="1.0" xmlns:jxb="http://java.sun.com/xml/ns/jaxb" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<jxb:bindings schemaLocation="../xsd/common/common.xsd" node="/xs:schema">
<jxb:bindings node="//xs:element[#name='api']">
<jxb:schemaBindings>
<jxb:package name="com.myxml.common.api" />
</jxb:schemaBindings>
</jxb:bindings>
</jxb:bindings>
</jxb:bindings>
My xsd file common.xsd is:
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" version="1.1" xml:lang="en">
<xs:element name='api'>
<xs:complexType>
<xs:simpleContent>
<xs:extension base="xs:string" />
</xs:simpleContent>
</xs:complexType>
</xs:element>
</xs:schema>
And I'm using the following Maven plugin to get everything going:
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>jaxb2-maven-plugin</artifactId>
<version>1.5</version>
<executions>
<execution>
<id>schema00-generate</id>
<phase>generate-sources</phase>
<goals>
<goal>xjc</goal>
</goals>
<configuration>
<schemaFiles>common/common.xsd</schemaFiles>
<bindingFiles>common.xjb</bindingFiles>
<bindingDirectory>${project.basedir}/src/main/xjb</bindingDirectory>
</configuration>
</execution>
</executions>
</plugin>
Why am I getting this error and how can I resolve it? I don't have any other bindings in use at this stage that I'm aware of.
Elements from the same namespace cannot be mapped to different packages so the package cannot be defined for anything other than the top level

Categories

Resources