jaxb2 & xjc - generate fields with inherited type - jaxb

I'm having some trouble with an xsd that was supplied to us. I'm not really sure how to describe my issue without example so I created a test xsd that has the same issue.
Here's the xsd:
<?xml version="1.0" encoding="UTF-8" ?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="shiporder" type="shipordertype"/> <!-- note: name is equal to name of a complex type further down -->
<xs:complexType name="shiptotype">
<xs:sequence>
<xs:element name="name" type="stringtype"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="myshiptotype">
<xs:complexContent>
<xs:restriction base="shiptotype">
<xs:sequence>
<xs:element name="name" type="stringtype">
<xs:annotation>
<xs:documentation source="Yellow Field"/>
</xs:annotation>
</xs:element>
</xs:sequence>
</xs:restriction>
</xs:complexContent>
</xs:complexType>
<xs:complexType name="shiporder">
<xs:sequence>
<xs:element name="orderperson" type="stringtype"/>
<xs:element name="shipto" type="shiptotype"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="shipordertype">
<xs:complexContent>
<xs:restriction base="shiporder">
<xs:sequence>
<xs:element name="orderperson" type="stringtype"/>
<xs:element name="shipto" type="myshiptotype">
<xs:annotation>
<xs:documentation source="Yellow Field"/>
</xs:annotation>
</xs:element>
</xs:sequence>
</xs:restriction>
</xs:complexContent>
</xs:complexType>
<xs:simpleType name="stringtype">
<xs:restriction base="xs:string"/>
</xs:simpleType>
</xs:schema>
I've got this external binding file:
<?xml version="1.0" encoding="UTF-8"?>
<jaxb:bindings xmlns:jaxb="http://java.sun.com/xml/ns/jaxb"
xmlns:xjc="http://java.sun.com/xml/ns/jaxb/xjc"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
version="2.1">
<jaxb:bindings schemaLocation="test.xsd" node="/xs:schema">
<jaxb:bindings node="//xs:complexType[#name='shiporder']">
<jaxb:class name="InnerShipOrder" />
</jaxb:bindings>
</jaxb:bindings>
</jaxb:bindings>
Now when I generate java classes I get the following classes:
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "shipordertype")
#XmlRootElement(name = "shiporder")
public class Shiporder
extends InnerShipOrder
implements Serializable
{
private final static long serialVersionUID = -1L;
}
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "shiporder", propOrder = {
"orderperson",
"shipto"
})
#XmlSeeAlso({
Shiporder.class
})
public class InnerShipOrder
implements Serializable
{
private final static long serialVersionUID = -1L;
#XmlElement(required = true)
protected String orderperson;
#XmlElement(required = true)
protected Shiptotype shipto;
// some getter and setter functions
Now, in the xsd the shipordertype has a 'shipto' field of the type 'myshiptotype'. However in the generated classes, shipordertype extends shiporder which has a 'shipto' field of the type 'shiptotype'.
Is it possible to make it so that the generated ShipOrder class has a 'shipto' field of the 'myshiptotype'?

Related

How to change XmlType of generated JAXB classes

I am using gradle to generate Java classes based on an XML Schema file. I am using 'org.glassfish.jaxb:jaxb-xjc:2.2.11' and 'org.glassfish.jaxb:jaxb-runtime:2.2.11' as dependencies so I can use the 'com.sun.tools.xjc.XJC2Task' class to generate the classes.
This is the schema file:
<?xml version="1.0" encoding="utf-8"?>
<xs:schema id="test"
targetNamespace="urn:oio:records:1.0.0"
xmlns="urn:oio:records:1.0.0"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
elementFormDefault="qualified">
<xs:element name="records" type="recordsType"/>
<xs:element name="record" type="recordType"/>
<xs:complexType name="recordsType">
<xs:sequence>
<xs:element ref="record" minOccurs="0" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="recordType">
<xs:attribute name="key" type="xs:string"/>
<xs:attribute name="value" type="xs:string"/>
</xs:complexType>
</xs:schema>
One of the generated classes look like this:
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlType;
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "recordType")
public class RecordType {
#XmlAttribute(name = "key")
protected String key;
#XmlAttribute(name = "value")
protected String value;
public String getKey() {return key;}
public void setKey(String value) {this.key = value;}
public String getValue() {return value;}
public void setValue(String value) {this.value = value;}
}
How can I change the name value in the #XmlType annotation? I would like it to be
#XmlType(name = "record")
I have tried using a bindingsfile and tried to experiment with the <javaType> tag in the bindingsfile, but without luck.
EDIT:
The reason I need to change this is that I need to split an XML file using the stax splitter from Camel (http://camel.apache.org/stax.html section called "Iterate over a collection using JAXB and StAX").
This looks at the name attribute of the #XmlType annotation to recognize the xml tag to split on in the file. The recognized tag (<record>) will then be JAXB parsed to a RecordType java class.
The name in the #XmlType annotation is the name of the complexType in your schema file. This is how the parameter 'name' is defined for this annotation.
So, if you want to change it, you have to change the name of the complexType in your schema:
<xs:complexType name="record">
<xs:attribute name="key" type="xs:string"/>
<xs:attribute name="value" type="xs:string"/>
</xs:complexType>
You can use the jaxb2-annotate-plugin to override the value of the name attribute in the generated Java class.
For your code, it would look like this:
<xs:schema id="test"
targetNamespace="urn:oio:records:1.0.0"
xmlns="urn:oio:records:1.0.0"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:annox="http://annox.dev.java.net"
jaxb:extensionBindingPrefixes="annox"
elementFormDefault="qualified">
<xs:element name="records" type="recordsType"/>
<xs:element name="record" type="recordType"/>
<xs:complexType name="recordsType">
<xs:sequence>
<xs:element ref="record" minOccurs="0" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="recordType">
<xs:annotation>
<xs:appinfo>
<annox:annotate target="class">
#javax.xml.bind.annotation.XmlType(name = "record")
</annox:annotate>
</xs:appinfo>
</xs:annotation>
<xs:attribute name="key" type="xs:string"/>
<xs:attribute name="value" type="xs:string"/>
</xs:complexType>
</xs:schema>
I don't know if this should be considered a hack, but it does the trick. Interestingly enough, in my case even the namespace attribute of XmlType is still generated and filled with the same value as without adding that explicit annotation.

JAXB schema to Java Different XmlRootElement name and Class name

I have a xsd schema from which I'm generating some java classes. I'm using jaxb for the generation.
I want to be able to generate a class annotated with #XmlRootElement, but I want the #XmlRootElement name property to be different than the name of the generated class.
In my xsd I'm defining the following:
<xs:element name="customer">
<xs:complexType>
<xs:sequence>
....
</xs:sequence>
</xs:complexType>
</xs:element>
This piece of code generates the following java class:
#XmlRootElement(name = "customer")
public class Customer {
...
}
The name property of the #XmlRootElement is the same as the name of the generated Class. I want the generated class name to be CustomerRequest.
I've tryed to use the jaxb:class definition to change the classe name. Indeed this option changes the class name but removes the #XmlRootElement annotation, and I need it to be present.
The following xsd:
<xs:element name="customer">
<xs:complexType>
<xs:annotation>
<xs:appinfo>
<jaxb:class name="CustomerRequest"/>
</xs:appinfo>
</xs:annotation>
<xs:sequence>
</xs:sequence>
</xs:complexType>
</xs:element>
Generates this class:
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "customer", propOrder = {
})
public class CustomerRequest {
}
How can I make the property name of the #XmlRootElement annotation different from the generated class name without loosing the annotation?
Solution update:
User Xstian proposed the correct solution using external bindings.
Just for further reference, I'll update my own post with the solution converted for using inline bindings:
<xs:element name="customer">
<xs:complexType>
<xs:annotation>
<xs:documentation>Request object for the operation that checks if a customer profile exists.</xs:documentation>
<xs:appinfo>
<annox:annotate>
<annox:annotate annox:class="javax.xml.bind.annotation.XmlRootElement" name="customer"/>
</annox:annotate>
<jaxb:class name="CustomerRequest"/>
</xs:appinfo>
</xs:annotation>
<xs:sequence>
</xs:sequece>
</xs:complexType>
</xs:element>
I suggest you to use this bindings
<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="../your.xsd">
<bindings node="//xs:element[#name='customer']//xs:complexType">
<annox:annotate>
<annox:annotate annox:class="javax.xml.bind.annotation.XmlRootElement"
name="customer" namespace="yourNamespaceIfYouWant">
</annox:annotate>
</annox:annotate>
<class name="CustomerRequest"/>
</bindings>
</bindings>
</bindings>
Class
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "", propOrder = {
"header"
})
#XmlRootElement(name = "customer", namespace = "yourNamespaceIfYouWant")
public class CustomerRequest

Reusable complexType, and xml-registry, xml-element-decl usage

I've the below TimePeriodType
<xs:simpleType name="UnitOfTimePeriodType">
<xs:restriction base="xs:token">
<xs:enumeration value="Months"/>
<xs:enumeration value="Days"/>
<xs:enumeration value="Years"/>
</xs:restriction>
</xs:simpleType>
<xs:complexType name="TimePeriodType">
<xs:sequence>
<xs:element name="Length" type="digit1-3"/>
</xs:sequence>
<xs:attribute name="unitOfLength" type="UnitOfTimePeriodType" use="optional" default="Months"/>
</xs:complexType>
which gets used in multiple types as shown below
<xs:complexType name="USAddressType">
<xs:complexContent>
<xs:extension base="AddressType">
<xs:sequence>
<xs:element name="LengthAtAddress" type="TimePeriodType" minOccurs="0"/>
</xs:sequence>
</xs:extension>
</xs:complexContent>
</xs:complexType>
<xs:complexType name="CanadaAddressType">
<xs:complexContent>
<xs:extension base="AddressType">
<xs:sequence>
<xs:element name="LengthAtAddress" type="TimePeriodType" minOccurs="0"/>
</xs:sequence>
</xs:extension>
</xs:complexContent>
</xs:complexType>
So, the below instance xml
<LengthAtAddress unitOfLength="Months">
<Length>36</Length>
</LengthAtAddress>
should be mapped to the following pojo
public abstract class AbstractAddress
{
protected int lengthAtAddress;
protected UnitOfLengthType unitOfLengthAtAddress;
public int getLengthAtAddress()
{
return lengthAtAddress;
}
public UnitOfLengthType getUnitOfLengthAtAddress()
{
return unitOfLengthAtAddress;
}
//setters
}
I think I should be using a combination of xml-registry and xml-element-decl while using external mapping. Not able to figure our how. Any help is appreciated.
FIX
<xml-element java-attribute="lengthAtAddress" xml-path="LengthAtAddress/Length/text()"/>
<xml-element java-attribute="unitOfLengthAtAddress" xml-path="LengthAtAddress/#unitOfLength">
<xml-java-type-adapter value="com.equifax.ic.platform.sts.domain.transformation.response.CommonsEnumAdapter"/>
</xml-element>
You could use MOXy's #XmlPath extension or the equivalent in MOXy's external mapping document.
#XmlPath("LengthAtAddress/Length/text()")
public int getLengthAtAddress()
{
return lengthAtAddress;
}

XSD byte[][] issue on Web Service

I'm trying to pass to my web service something like an array of byte array (byte[][]) to do that i created this xsd file:
<?xml version="1.0" encoding="utf-8"?>
<xs:schema id="FileResponse"
targetNamespace="http://tempuri.org/FileResponse.xsd"
elementFormDefault="qualified"
xmlns="http://tempuri.org/FileResponse.xsd"
xmlns:mstns="http://tempuri.org/FileResponse.xsd"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
>
<xs:complexType name="FileResponse">
<xs:sequence>
<xs:element name="SPResultItem" maxOccurs="unbounded">
<xs:complexType>
<xs:sequence>
<xs:element name="file" type="xs:unsignedByte" minOccurs="0" maxOccurs="unbounded"></xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="Message" type="xs:string"></xs:element>
</xs:sequence>
</xs:complexType>
<xs:element name="FileResponse" type="FileResponse"/>
</xs:schema>
XSD tool generates this:
/// <remarks/>
[System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "2.0.50727.3038")]
[System.SerializableAttribute()]
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlTypeAttribute(Namespace="http://tempuri.org/FileResponse.xsd")]
[System.Xml.Serialization.XmlRootAttribute(Namespace="http://tempuri.org/FileResponse.xsd", IsNullable=false)]
public partial class FileResponse {
private FileResponseSPResultStatus sPResultStatusField;
private sbyte[][] sPResultItemField;
private string messageField;
/// <remarks/>
public FileResponseSPResultStatus SPResultStatus {
get {
return this.sPResultStatusField;
}
set {
this.sPResultStatusField = value;
}
}
/// <remarks/>
[System.Xml.Serialization.XmlArrayItemAttribute("file", typeof(sbyte), IsNullable=false)]
public sbyte[][] SPResultItem {
get {
return this.sPResultItemField;
}
set {
this.sPResultItemField = value;
}
}
/// <remarks/>
public string Message {
get {
return this.messageField;
}
set {
this.messageField = value;
}
}
}
As you can see, the conversion seems good: the xsd contains a "sbyte[][]" field... But when i call a method from this webservice (even if it doesn't use this class) it crashes without giving infos...
How can i create an XSD that receives a sbyte[][] or byte[][] and works? Thank you very much!!
I solved this way:
<?xml version="1.0" encoding="utf-8"?>
<xs:schema id="FileResponse"
targetNamespace="http://tempuri.org/FileResponse.xsd"
elementFormDefault="qualified"
xmlns="http://tempuri.org/FileResponse.xsd"
xmlns:mstns="http://tempuri.org/FileResponse.xsd"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
>
<xs:complexType name="FileResponse">
<xs:sequence>
<xs:element name="SPResultItem" type="fileList" maxOccurs="unbounded"> </xs:element>
<xs:element name="Message" type="xs:string"></xs:element>
</xs:sequence>
<xs:complexType name ="fileList">
<xs:sequence>
<xs:element name="file" type="xs:base64Binary" ></xs:element>
</xs:sequence>
</xs:complexType>
<xs:element name="FileResponse" type="FileResponse"/>

Polymorphism in XSD schema and JAXB classes

I have an xml like this:
<todo>
<doLaundry cost="1"/>
<washCar cost="10"/>
<tidyBedroom cost="0" experiencePoints="5000"/>
</todo>
And the XSD schema for it is:
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:complexType name="todo">
<xs:sequence>
<xs:choice maxOccurs="unbounded">
<xs:element name="doLaundry" type="doLaundry" />
<xs:element name="washCar" type="washCar" />
<xs:element name="tidyBedroom" type="tidyBedroom" />
</xs:choice>
</xs:sequence>
</xs:complexType>
<xs:complexType name="doLaundry">
<xs:attribute name="cost" type="xs:int" />
</xs:complexType>
<xs:complexType name="washCar">
<xs:attribute name="cost" type="xs:int" />
</xs:complexType>
<xs:complexType name="tidyBedroom">
<xs:attribute name="cost" type="xs:int" />
<xs:attribute name="experiencePoints" type="xs:int" />
</xs:complexType>
</xs:schema>
And when I process this schema through JAXB I get a class with a method like this:
public class Todo {
public List<Object> getDoLaundryOrWashCarOrTidyBedroom() {
...
}
}
Ideally, what I would like is a way to define a generic base type that all the other XSD types extend. The Jaxb classes generated from the XSD schema should have a method to return a list of generic tasks. This would make it very easy to add new tasks to the todo list:
public class Todo {
public List<Task> getTasks() {
...
}
}
public abstract class Task {
public int getCost() {
...
}
}
public class TidyBedroom extends Task {
public int getExperiencePoints() {
...
}
}
What should the XSD schema look like in order to generate the above Java classes?
I found the answer with the help of Blaise Doughan's article here: http://bdoughan.blogspot.com/2010/11/jaxb-and-inheritance-using-xsitype.html
This schema:
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:complexType name="todo">
<xs:sequence>
<xs:choice maxOccurs="unbounded">
<xs:element name="doLaundry" type="doLaundry" />
<xs:element name="washCar" type="washCar" />
<xs:element name="tidyBedroom" type="tidyBedroom" />
</xs:choice>
</xs:sequence>
</xs:complexType>
<xs:complexType abstract="true" name="Task">
<xs:attribute name="cost" type="xs:int" use="required" />
</xs:complexType>
<xs:complexType name="doLaundry">
<xs:complexContent>
<xs:extension base="Task">
</xs:extension>
</xs:complexContent>
</xs:complexType>
<xs:complexType name="washCar">
<xs:complexContent>
<xs:extension base="Task">
</xs:extension>
</xs:complexContent>
</xs:complexType>
<xs:complexType name="tidyBedroom">
<xs:complexContent>
<xs:extension base="Task">
<xs:attribute name="experiencePoints" type="xs:int" />
</xs:extension>
</xs:complexContent>
</xs:complexType>
</xs:schema>
combined with a binding file:
<jxb:bindings version="1.0" xmlns:jxb="http://java.sun.com/xml/ns/jaxb" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<jxb:bindings>
<jxb:bindings schemaLocation="todo.xsd" node="/xs:schema/xs:complexType[#name='todo']/xs:sequence/xs:choice">
<jxb:property name="Tasks"/>
</jxb:bindings>
</jxb:bindings>
</jxb:bindings>
Will give abstract and inherited classes as I described in the question. The binding file will change Jaxb's default method name from getDoLaundryOrWashCarOrTidyBedroom() to getTasks().
xsd:choice corresponds to the #XmlElements annotation. You could apply this annotation directly to your desired object model.
For more information see:
http://bdoughan.blogspot.com/2010/10/jaxb-and-xsd-choice-xmlelements.html
Use xs:extension in your schema and your JAXB classes will be inherited (extended) as you define in your schema.
Maybe I'm not 'getting' the question, but what is wrong with..
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:complexType name="todo">
<xs:sequence>
<xs:choice maxOccurs="unbounded">
<xs:element name="doLaundry" type="task" />
<xs:element name="washCar" type="task" />
<xs:element name="tidyBedroom" type="task" />
</xs:choice>
</xs:sequence>
</xs:complexType>
<xs:complexType name="task">
<xs:attribute name="cost" type="xs:int" />
<xs:attribute name="experiencePoints" type="xs:int" />
</xs:complexType>
</xs:schema>

Resources