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.
Related
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?
I have the following XML schema:
<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<xsd:element name="cg" type="cgType"/>
<xsd:complexType name="cgType">
<xsd:sequence>
<xsd:element name="code" type="upperCaseString" minOccurs="1" maxOccurs="1"/>
<xsd:element name="action" type="cgAction" minOccurs="1" maxOccurs="1"/>
<xsd:element name="item" minOccurs="0" maxOccurs="unbounded">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="param" type="xsd:string" minOccurs="0" maxOccurs="unbounded" nillable="true"/>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
</xsd:sequence>
<xsd:attribute name="automated" type="xsd:boolean" default="false" use="optional"/>
</xsd:complexType>
<xsd:simpleType name="cgAction">
<xsd:restriction base="xsd:string">
<xsd:enumeration value="TAKE"/>
<xsd:enumeration value="CLEAR"/>
</xsd:restriction>
</xsd:simpleType>
<xsd:simpleType name="upperCaseString">
<xsd:restriction base="xsd:string">
<xsd:pattern value="[A-Z]*"/>
</xsd:restriction>
</xsd:simpleType>
</xsd:schema>
EDIT: I also have the following global bindings:
<?xml version="1.0" encoding="UTF-8" ?>
<jaxb:bindings
xmlns:jaxb="http://java.sun.com/xml/ns/jaxb"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xjc="http://java.sun.com/xml/ns/jaxb/xjc"
jaxb:version="2.1">
<jaxb:bindings schemaLocation="cg.xsd" node="/xsd:schema">
<jaxb:globalBindings>
<xjc:simple/>
</jaxb:globalBindings>
</jaxb:bindings>
</jaxb:bindings>
Using XJC to generate classes works as expected, with a class named Cg that has both #XmlType and #XmlRootElement annotations. I want to rename that class to be CG, so I changed the binding file to be:
<?xml version="1.0" encoding="UTF-8" ?>
<jaxb:bindings
xmlns:jaxb="http://java.sun.com/xml/ns/jaxb"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xjc="http://java.sun.com/xml/ns/jaxb/xjc"
jaxb:version="2.1">
<jaxb:bindings schemaLocation="cg.xsd" node="/xsd:schema">
<jaxb:schemaBindings>
<jaxb:package name="foo.bar.cg"/>
</jaxb:schemaBindings>
<jaxb:bindings node="//xsd:element[#name='cg']">
<jaxb:class name="CG"/>
</jaxb:bindings>
<jaxb:bindings node="//xsd:complexType[#name='cgType']">
<jaxb:class name="CG"/>
</jaxb:bindings>
</jaxb:bindings>
</jaxb:bindings>
This causes XJC to complain about a collision in the ObjectFactory class between the two CG classes. If I remove the binding on the xsd:element, XJC will now create a CG class as expected, however it misses out the #XmlRootElement annotation. I've experimented with various combinations of bindings on the element and complexType but have been unable to simply rename the default generated class.
How can I rename both the element and the complexType at once?
Both the element and complexType are their own java classes. Using no binding file you should get the result of 2 class files: Cg.java and CgType.java. In your bindings your trying to rename both of those to CG.java which of course is not possible.
What you are probably looking for is to rename the complexType to CGType in which case your bindings should read:
...
<jaxb:bindings node="//xsd:element[#name='cg']">
<jaxb:class name="CG"/>
</jaxb:bindings>
<jaxb:bindings node="//xsd:complexType[#name='cgType']">
<jaxb:class name="CGType"/>
</jaxb:bindings>
...
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
I've an XSD file containing this:
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:jaxb="http://java.sun.com/xml/ns/jaxb"
xmlns:xjc="http://java.sun.com/xml/ns/jaxb/xjc"
jaxb:extensionBindingPrefixes="xjc"
elementFormDefault="qualified"
targetNamespace="http://example.org/">
<xsd:complexType name="Certificate">
<xsd:sequence>
<xsd:element name="certificate" type="xsd:base64Binary">
<xsd:annotation>
<xsd:appinfo>
<xjc:javaType name="java.security.cert.X509Certificate" adapter="adapters.X509CertificateAdapter" />
</xsd:appinfo>
</xsd:annotation>
</xsd:element>
</xsd:sequence>
</xsd:complexType>
</xsd:schema>
and when I generate java code with xjc, it produces this:
public class Certificate {
#XmlElement(required = true, type = String.class)
#XmlJavaTypeAdapter(X509CertificateAdapter.class)
#XmlSchemaType(name = "base64Binary")
protected X509Certificate certificate;
....
}
and the adapter works fine.
My question is what will happen when I will give my xsd to a no-java client? What will the Certificate C# class (for example) look like?
You should leave a XSD file as clean as possible by the technology used.
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
elementFormDefault="qualified"
targetNamespace="http://example.org/">
<xsd:complexType name="Certificate">
<xsd:sequence>
<xsd:element name="certificate" type="xsd:base64Binary">
</xsd:element>
</xsd:sequence>
</xsd:complexType>
</xsd:schema>
Java-side, to generate the extra information you should use bindings file (XJB).
Following an example of adding an annotation.
<bindings version="2.0" xmlns="http://java.sun.com/xml/ns/jaxb"
xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xjc="http://java.sun.com/xml/ns/jaxb/xjc"
xmlns:annox="http://annox.dev.java.net"
xmlns:namespace="http://jaxb2-commons.dev.java.net/namespace-prefix">
<bindings schemaLocation="../yourSchema.xsd">
<bindings node="//xs:complexType[#name='User']">
<annox:annotate>
<annox:annotate annox:class="javax.xml.bind.annotation.XmlRootElement"
name="User">
</annox:annotate>
</annox:annotate>
</bindings>
</bindings>
</bindings>
In this case you need to change this xsd to make it compatible with some other system, becouse you are using specific namespace for java tecnologies.
(i.e. http://java.sun.com/xml/ns/jaxb/xjc)
<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>