CXF not adding superclass to generated class - java

I try to add a superclass definition to a generated class based on an incoming XML complexType. When I make the mapping generic, like this, it works:
<jaxb:globalBindings generateElementProperty="false">
<xjc:superClass name="nl.ilent.bpo.interceptor.Hashable" />
</jaxb:globalBindings>
Now I want only one type to subclass this class. I tried many selectors, but this is the only one that finds exactly one type:
<jaxb:bindings schemaLocation="../xsd/OrganisatieTypes.xsd" node="//xs:complexType[#name='DatumIncompleetType']">
<xjc:superClass name="nl.ilent.bpo.interceptor.Hashable" />
</jaxb:bindings>
Even though many alternatives gave a "0 nodes" result, and this one does not, the generated class does not subclass Hashable. What am I doing wrong?
This is the XSD defining the complexType in ../xsd/OrganisatieTypes.xsd:
<xsd:complexType name="DatumIncompleetType">
<xsd:choice minOccurs="0">
<xsd:element name="datum" type="xsd:date"/>
<xsd:element name="jaarMaand" type="xsd:gYearMonth"/>
<xsd:element name="jaar" type="xsd:gYear"/>
</xsd:choice>
</xsd:complexType>
We are using CXF 2.7.18

Try this way
<jaxb:bindings schemaLocation="../xsd/OrganisatieTypes.xsd" node="/xs:schema">
<jaxb:bindings>
<jaxb:bindings node="//xs:complexType[#name='DatumIncompleetType']">
<xjc:superClass name="nl.ilent.bpo.interceptor.Hashable" />
</jaxb:bindings>
</jaxb:bindings>
</jaxb:bindings>
Edit
Ok, sorry, that won't work: according to https://docs.oracle.com/cd/E17802_01/webservices/webservices/docs/2.0/jaxb/vendorCustomizations.html#superclass this is only possible for global bindings, i.e. for all generated classes, not for single generated classes

Related

Can you customise JAXB to generate top level classes for the nested elements of one specific element?

I have a xsd schema with huge nested elements (no types are defined), and as a consequence one of the JAXB classes generated by xjc is to big for my IDE to parse. So what I'm looking for is a solution to reduce the size of the classes generated, ideally by generating the nested elements as top-level classes.
As a first approach, I've tried <jaxb:globalBindings localScoping="toplevel"/>, but this leads to inevitable name collisions. So I've tried to apply this localScoping to a single element, but failed to do so.
Following is a simplified illustration of my attempt.
foo.xsd
<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<xsd:element name="foo">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="bar" minOccurs="0" type="xsd:string"/>
<!-- ... -->
</xsd:sequence>
</xsd:complexType>
</xsd:element>
<xsd:element name="bar">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="baz" minOccurs="0" type="xsd:string"/>
<!-- ... -->
</xsd:sequence>
</xsd:complexType>
</xsd:element>
</xsd:schema>
foo.xjb
<?xml version="1.0" encoding="UTF-8"?>
<jaxb:bindings version="2.1" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:jaxb="http://java.sun.com/xml/ns/jaxb">
<jaxb:bindings schemaLocation="foo.xsd">
<jaxb:schemaBindings>
<jaxb:package name="com.example.foo" />
</jaxb:schemaBindings>
<jaxb:bindings node="/xsd:schema/xsd:element[#name='bar']">
<jaxb:globalBindings localScoping="toplevel" />
<jaxb:schemaBindings>
<jaxb:package name="com.example.foo.bar" />
</jaxb:schemaBindings>
</jaxb:bindings>
</jaxb:bindings>
</jaxb:bindings>
console output
$ xjc -b foo.xjb foo.xsd
parsing a schema...
[ERROR] compiler was unable to honor this schemaBinding customization. It is attached to a wrong place, or its inconsistent with other bindings.
line 10 of file:foo.xjb
[ERROR] (the above customization is attached to the following location in the schema)
line 11 of file:foo.xsd
Failed to parse a schema.
How can I achieve the desired customization? Or is there another workaround?

Is this offical Xml schema invalid or is error limitation of Jaxb

uPnP defines a number of Xml schemas including didl-lite.xsd, including this section:
<xsd:sequence>
<xsd:element ref="dc:title"/>
<xsd:group ref="didl-lite:allowed-under-container" minOccurs="0" maxOccurs="unbounded"/>
<xsd:group ref="upnp:class.group"/>
<xsd:group ref="didl-lite:allowed-under-container" minOccurs="0" maxOccurs="unbounded"/>
</xsd:sequence>
When I tried to build Java classes from this using jaxb it complained
Removing the second xsd:group ref="didl-lite:allowed-under-container" minOccurs="0" maxOccurs="unbounded" line so we have
<xsd:sequence>
<xsd:element ref="dc:title"/>
<xsd:group ref="didl-lite:allowed-under-container" minOccurs="0" maxOccurs="unbounded"/>
<xsd:group ref="upnp:class.group"/>
</xsd:sequence>
fixed the issue, and seems to make more sense.
But I am not clear is the Xsd actually invalid or is this just a limitation of generating Jaxb classes from Xsd?
I think both semantically and formally the schema provided is valid.
You can, for instance, verify the schema well-formedness with Java or online, for example, in this site.
The issue you are facing could be considered a kind of limitation of JAXB.
The limitation consists in that the generator encounters a value that has been already taken into consideration in the process of generating your classes, and it has a problem, because it will be unable to generate a property and the corresponding related methods and stuff for this second value because the names are already taken.
I will use the xjc tool for code generation but the solution should be portable to the Maven or Gradle plugins too.
If you run the xjc tool like this:
xjc -d out didl-lite-v2.xsd
the error description will give you a possible solution:
[ERROR] Property "AllowedUnderItem" is already defined. Use <jaxb:property> to resolve this conflict
The mentioned term <jaxb:property> has to do with JAXB XML bindings.
JAXB XML bindings allows you to customize the JAXB Java classes generation process in different ways.
The necessary configuration is provided in a XML file with a certain information.
In this specific use case, you can define the following bindings XML file, let's name it binding.xml:
<?xml version="1.0" encoding="UTF-8"?>
<jaxb:bindings
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:jaxb="http://java.sun.com/xml/ns/jaxb"
version="2.1">
<jaxb:bindings schemaLocation="didl-lite-v2.xsd">
<jaxb:bindings
node="//xsd:complexType[#name='container.type']/xsd:sequence/xsd:group[#ref='didl-lite:allowed-under-container'][2]">
<jaxb:property name="allowedUnderContainerAfterUpnpClassGroup"/>
</jaxb:bindings>
<jaxb:bindings
node="//xsd:complexType[#name='item.type']/xsd:sequence/xsd:group[#ref='didl-lite:allowed-under-item'][2]">
<jaxb:property name="allowedUnderItemAfterUpnpClassGroup"/>
</jaxb:bindings>
</jaxb:bindings>
</jaxb:bindings>
As you can see, we are indicating that the second occurrence of the allowed-under-item group, represented by the XPath expression //xsd:complexType[#name='item.type']/xsd:sequence/xsd:group[#ref='didl-lite:allowed-under-item'][2], should be treated as allowedUnderItemAfterUpnpClassGroup. We need to do something similar with allowed-under-container.
Then, if you run again the xjc tool passing in this XML bindings file, your classes will be generated successfully:
xjc -d out -b binding.xml didl-lite-v2.xsd
This or this other SO questions could be of help as well.

how to rename nested classes in jaxb xjc

I have a wsdl that imports a schema
I am trying to resolve xjc naming multiple nested MyElementName classes with the same name - which doesn't compile
I have created this binding file below but it gives the error:
parsing a schema...
compiling a schema...
Exception in thread "main" java.lang.IllegalArgumentException: Illegal class inheritance loop.
Outer class MyElementName1 may not subclass from inner class: MyElementName1
at com.sun.codemodel.internal.JDefinedClass._extends(JDefinedClass.java:257)
at com.sun.tools.internal.xjc.generator.bean.ImplStructureStrategy$1._extends(ImplStructureStrategy.java:104)
at com.sun.tools.internal.xjc.generator.bean.BeanGenerator.<init>(BeanGenerator.java:197)
at com.sun.tools.internal.xjc.generator.bean.BeanGenerator.generate(BeanGenerator.java:151)
at com.sun.tools.internal.xjc.model.Model.generateCode(Model.java:275)
at com.sun.tools.internal.xjc.Driver.run(Driver.java:342)
at com.sun.tools.internal.xjc.Driver.run(Driver.java:184)
at com.sun.tools.internal.xjc.Driver._main(Driver.java:107)
at com.sun.tools.internal.xjc.Driver.access$000(Driver.java:64)
at com.sun.tools.internal.xjc.Driver$1.run(Driver.java:87)
I I remove the first binding for MyElementName1 it works correctly but only names 1 of the classes, I want to change both names,
how do I change the binding file so that it will properly name both old nested classes to two unique names I have picked below. I want to do this because I have other mappings with 4 nested classes with the same name so being able to name each class would be needed to tackle the more nested problem areas
<jaxb:bindings
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:jaxb="http://java.sun.com/xml/ns/jaxb"
version="2.1">
<jaxb:bindings schemaLocation="MYWSDL.wsdl">
<jaxb:bindings node="//xsd:element[#name='AAAA']//xsd:complexType//xsd:sequence/xsd:element[#name='BBB']//xsd:complexType/xsd:sequence//xsd:element[#name='MyElementName']">
<jaxb:class name="MyElementName1"/>
</jaxb:bindings>
<jaxb:bindings node="//xsd:element[#name='AAAA']//xsd:sequence/xsd:element[#name='BBB']//xsd:element[#name='MyElementName']//xsd:sequence/xsd:element[#name='CCC']//xsd:sequence/xsd:element[#name='MyElementName']/xsd:complexType">
<jaxb:class name="MyElementName2"/>
</jaxb:bindings>
</jaxb:bindings>
</jaxb:bindings>
here is xsd (names of fields changed but rest same)
<?xml version="1.0" encoding="utf-8"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<xsd:element minOccurs="0" name="AAA">
<xsd:complexType>
<xsd:element minOccurs="0" name="BBB">
<xsd:complexType>
<xsd:element name="MyElementName">
<xsd:complexType>
<xsd:sequence>
<xsd:element minOccurs="0" name="CCC">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="MyElementName">
<xsd:complexType>
<xsd:sequence>
<xsd:element ref="FromDate" />
<xsd:element ref="ThruDate" />
</xsd:sequence>
</xsd:complexType>
</xsd:element>
<xsd:element name="DDD">
<xsd:complexType>
<xsd:sequence>
<xsd:element ref="DueDate" />
</xsd:sequence>
</xsd:complexType>
</xsd:element>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
<xsd:element minOccurs="0" ref="YYYY" />
<xsd:element minOccurs="0" ref="ZZZZ" />
</xsd:element>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
</xsd:complexType>
</xsd:element>
</xsd:complexType>
</xsd:element>
</xsd:schema>
the field MyElementName2 is ok, that gets built into the java file if I try binding below, its just the first one that jaxb is mapping to the multiple children - including the one that is mapped with MyElementName2. XPath always maps to one node so maybe its a jaxb option, as it seems to be expanding the input
here is the alternative binding that results in its error below
<jaxb:bindings
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:jaxb="http://java.sun.com/xml/ns/jaxb"
version="2.1">
<jaxb:bindings schemaLocation="MYWSDL.wsdl">
<jaxb:bindings node="//xsd:element[#name='AAA']//xsd:sequence/xsd:element[#name='BBB']//xsd:element[#name='MyElementName']">
<jaxb:class name="MyElementName1"/>
</jaxb:bindings>
<jaxb:bindings node="//xsd:element[#name='AAA']//xsd:sequence/xsd:element[#name='BBB']//xsd:element[#name='Billing']//xsd:sequence/xsd:element[#name='CCC']//xsd:sequence/xsd:element[#name='MyElementName']/xsd:complexType">
<jaxb:class name="MyElementName2"/>
</jaxb:bindings>
</jaxb:bindings>
</jaxb:bindings>
error:
parsing a schema...
[ERROR] XPath evaluation of "//xsd:element[#name='AAA']//xsd:sequence/xsd:element[#name='BBB']//xsd:element[#name='MyElementName']" results in too many target nodes
Failed to parse a schema.
JAXB generates one class per one complex type, so I believe you should bind your custom class name to xs:complexType rather than xs:element. Your first XPath refers to the xs:element and I think JAXB does not know how to bind a class to it. Try this binding:
<jaxb:bindings
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:jaxb="http://java.sun.com/xml/ns/jaxb"
version="2.1">
<jaxb:bindings schemaLocation="MYWSDL.wsdl">
<jaxb:bindings node="//xsd:element[#name='AAAA']//xsd:complexType//xsd:sequence/xsd:element[#name='BBB']//xsd:complexType/xsd:sequence//xsd:element[#name='MyElementName']/xs:complexType">
<jaxb:class name="MyElementName1"/>
</jaxb:bindings>
<jaxb:bindings node="//xsd:element[#name='AAAA']//xsd:sequence/xsd:element[#name='BBB']//xsd:element[#name='MyElementName']//xsd:sequence/xsd:element[#name='CCC']//xsd:sequence/xsd:element[#name='MyElementName']/xsd:complexType">
<jaxb:class name="MyElementName2"/>
</jaxb:bindings>
</jaxb:bindings>
Personally, I prefer using JAXB to XMLBeans. You should be careful with the latter, because when compiling a deeply nested schema it may generate a structure of nested classes impossible to compile due to filename length limitations (usually 255 characters).
Your XPath:
//xsd:element[#name='AAA']
//xsd:sequence/xsd:element[#name='BBB']
//xsd:element[#name='MyElementName']
Seems to refer to both of your MyElementName elements since they're both under BBB. Try to get rid of //.
//xsd:element[#name='AAA']
/xsd:complexType
/xsd:sequence
/xsd:element[#name='BBB']
/xsd:complexType
/xsd:sequence
/xsd:element[#name='MyElementName']
I think this is the right direction you go. You have to customize your anonymous inner complex types.

specify type for IDREF in XML schema

I am generating Java objects from an XML schema using xjc. I would like to reference the same element multiple times within the document using IDREF. I would also like to constrain the objects referenced by IDREF to a specific type. I'd like to do this for the purposes of schema validation, but also so that in the Java code, the referenced object is returned as a specific type instead of type Object. For example, say I want a schema to describe the following:
<teams>
<team id="team1">
<coach>coachz</coach>
<player>homestar</player>
<player>marzipan</player>
<player>strongsad</player>
<player>strongbad</player>
</team>
<team id="team2">
<coach>bubs</coach>
<player>homesar</player>
<player>thecheat</player>
<player>poopsmith</player>
<player>bubs</player>
</team>
<team id="allstars">
<coach>poopsmith</coach>
<player>coachz</player>
<player>bubs</player>
<player>kingoftown</player>
<player>strongbad</player>
</team>
</teams>
<people>
<person id="coachz">Coach Z</person>
<person id="homesar">Homesar</person>
<person id="homestar">Homestar</person>
<person id="strongbad">Strong Bad</person>
<person id="strongsad">Strong Sad</person>
<person id="marzipan">Marzipan</person>
<person id="bubs">Bubs</person>
<person id="kingoftown">King of Town</person>
<person id="poopsmith">The Poopsmith</person>
<person id="thecheat">The Cheat</person>
</people>
I can define player like this:
<xs:element name="player" type="xs:IDREF" maxOccurs="unbounded"/>
but then in the Java code, when I try to retrieve a player it will come back as type object, and I have to cast it to a person. At that point, if someone has mistakenly referenced a Team object, I have errors to deal with that could have been caught at validation. I want to specify something like this:
<xs:element name="player" type="xs:IDREF"reftype="person"maxOccurs="unbounded" />
But as far as I can tell, there is no way to specify a type as I have done here with the contrived attribute 'reftype'. Can this be done, using IDREF? If not, is there another method?
You can simply apply baseType binding to your player element. Something like:
<jaxb:bindings node="xsd:element[#name='player']">
<jaxb:property>
<jaxb:baseType name="....Person"/>
</jaxb:property>
</jaxb:bindings>
You may need to figure out the correct binding location for your schema.
Example from my code:
Schema:
<xsd:complexType name="HJIII-53-A">
<xsd:sequence>
<xsd:element name="b" type="xsd:IDREF"/>
<xsd:element name="b1" type="test:HJIII-53-B"/>
<xsd:element name="c" type="xsd:IDREFS"/>
<xsd:element name="c1" type="test:HJIII-53-C" minOccurs="0" maxOccurs="unbounded"/>
</xsd:sequence>
</xsd:complexType>
Bindings:
<jaxb:bindings schemaLocation="schema.xsd" node="/xsd:schema">
<jaxb:globalBindings localScoping="toplevel">
<jaxb:serializable/>
</jaxb:globalBindings>
<jaxb:bindings node="xsd:complexType[#name='HJIII-53-A']//xsd:element[#name='b']">
<jaxb:property>
<jaxb:baseType name="org.jvnet.hyperjaxb3.ejb.tests.issuesjpa2.HJIII53B"/>
</jaxb:property>
</jaxb:bindings>
<jaxb:bindings node="xsd:complexType[#name='HJIII-53-A']//xsd:element[#name='c']">
<jaxb:property>
<jaxb:baseType name="org.jvnet.hyperjaxb3.ejb.tests.issuesjpa2.HJIII53C"/>
</jaxb:property>
</jaxb:bindings>
</jaxb:bindings>
Generated code:
#XmlElement(required = true, type = Object.class)
#XmlIDREF
#XmlSchemaType(name = "IDREF")
protected org.jvnet.hyperjaxb3.ejb.tests.issuesjpa2.HJIII53B b;
#XmlElement(required = true)
protected org.jvnet.hyperjaxb3.ejb.tests.issuesjpa2.HJIII53B b1;
#XmlList
#XmlElement(required = true, type = Object.class)
#XmlIDREF
protected List<org.jvnet.hyperjaxb3.ejb.tests.issuesjpa2.HJIII53C> c;
protected List<org.jvnet.hyperjaxb3.ejb.tests.issuesjpa2.HJIII53C> c1;
See: https://svn.java.net/svn/hj3~svn/trunk/ejb/tests/issues-jpa2/src/main/resources/
lexicore's answer gave me what I need (and I suggest voting up his answer over mine). However I am using inline annotations instead of a separate bindings file. This is what it looks like with inline annotations, using my Homestar example:
<xs:element name="player" type="xs:IDREF" maxoccurs="unbounded">
<xs:annotation>
<xs:appinfo>
<jaxb:property>
<jaxb:baseType name="Person"/>
</jaxb:property>
</xs:appinfo>
</xs:annotation>
</xs:element>

marshall with xjc created nested classes

<ProductInformation Context="GL">
<Assets>
<Asset ID="assetID" UserTypeID="ID">
<Name>name</Name>
<Reference ClassificationID="id"/>
<Values>
<Value AttributeID="ID">Value1</Value>
<Value AttributeID="ID">Value2</Value>
<MultiValue AttributeID="attributeID">
<Value>value3a</Value>
<Value>value3b</Value>
</MultiValue>
</Values>
</Asset>
</Assets>
<Products>....</Products>
</ProductInformation>
I used this xml->xsd and xjc to create classes from it.
Now I want to create my ProductInformation object,and marshall it.
My problem is xjc create 3 classes and a objectfactory, and some nested classes inside ProductInformation. When I look at the avaliable methods I mostly see getters instead of setters.
"Asset" class has no such methods like;
asset.setValues(List<Value> values)
Also I ended up writing funny code like this;
ProductInformation.Assets.Asset.Values.MultiValue multivalue=new ProductInformation.Assets.Asset.Values.MultiValue();
Is this normal with Jaxb?
The answer given by Ian Roberts is the correct one. I am giving this one to provide some additional information for those people interested in not having inner classes.
XML Schema (schema.xsd)
If JAXB classes are generated from the following XML schema, both the resulting Customer and Employee classes will contain a static nested class called Address (because each contains their own definition of an address). This is in fact why static nested classes are used to avoid name conflict problems.
<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema
targetNamespace="http://www.example.org/company"
xmlns="http://www.example.org/company"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
elementFormDefault="qualified">
<xsd:element name="customer">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="address">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="street" type="xsd:string"/>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
<xsd:element name="employee">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="address">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="road" type="xsd:string"/>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
</xsd:schema>
JAXB Binding File (binding.xml)
A bindings file is used to customize the schema to Java generation. You can specify that everything should be a top level class with localScoping="top-level". When you do this you must make sure to resolve any potential name conflicts.
<jaxb:bindings
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:jaxb="http://java.sun.com/xml/ns/jaxb"
version="2.1">
<jaxb:globalBindings localScoping="toplevel"/>
<jaxb:bindings schemaLocation="company.xsd">
<jaxb:bindings node="//xsd:element[#name='employee']/xsd:complexType/xsd:sequence/xsd:element[#name='address']/xsd:complexType">
<jaxb:class name="EmployeeAddress"/>
</jaxb:bindings>
</jaxb:bindings>
</jaxb:bindings>
XJC Call
Below is an example of specifying a bindings file when using the XJC command to generate Java classes from an XML schema.
xjc -b binding.xml schema.xsd
For More Information
http://blog.bdoughan.com/2011/07/jaxb-xjc-and-nested-classes.html
The way JAXB normally handles multi valued properties is to provide just a getter and no setter for the List<Whatever>, which returns a mutable list - you're supposed to call the getter to retrieve an initially-empty list and then create the member objects for this list using new in the normal way and add them directly to the list. You can new a static nested class in exactly the same way as a top-level one.
Single-valued properties (not lists) should have been generated with both getter and setter.
This is actually just a comment to Blaise Doughan's answer but I want to post the xml.
If you work with a more complex xsd and the path in the node attribute is getting too long, you can:
<jaxb:bindings xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:jaxb="http://java.sun.com/xml/ns/jaxb" version="2.1">
<jaxb:globalBindings localScoping="toplevel"/>
<jaxb:bindings schemaLocation="company.xsd">
<jaxb:bindings node="//xsd:element[#name='employee']">
....
<jaxb:bindings node=".//xsd:element[#name='address']/xsd:complexType">
<jaxb:class name="EmployeeAddress"/>
</jaxb:bindings>
....
</jaxb:bindings>
</jaxb:bindings>
</jaxb:bindings>

Categories