How to generate classes from XSD that implements serializable? - java

I need to generate many classes from my XML Schema (XSD) in a package (.jar).
How can I configure these classes to be serializable?
(I'm using Eclipse and JAX-B)

If you are using XJC, I recomend you to read this reference: JavaTM Architecture for XML Binding: JAXB RI Vendor Extensions Customizations :
You have to add in your schema aditional namespaces definition to add xjc aditional markup:
<xs:schema xmlns:xs="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"
jaxb:version="1.0">
Then, including an <xjc:serializable> node within <jaxb:globalBindings>:
<xs:annotation>
<xs:appinfo>
<jaxb:globalBindings generateIsSetMethod="true">
<xjc:serializable uid="12343"/>
</jaxb:globalBindings>
</xs:appinfo>
</xs:annotation>
This will cause that all the concrete classes implement the Serializable interface. Also, you can define the UUID value of the resulting classes (that's an optional attribute).

I've found
<schema
xmlns="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"
jaxb:version="1.0"
>
<!-- FORCE ALL CLASSES IMPLEMENTS SERIALIZABLE -->
<annotation>
<appinfo>
<jaxb:globalBindings generateIsSetMethod="true">
<xjc:serializable uid="1"/>
</jaxb:globalBindings>
</appinfo>
</annotation>
....
</schema>

Related

Jaxb: Generate constant value for fixed-value attribute

I'm currently working on a xsd which uses the following contruct:
<xs:attribute name="listVersionID" type="xs:normalizedString" use="required" fixed="1.0">
While not problematic per se, it is rather annoying to work with, since the fixed-value of this definition increases between releases of the xsd spec, and we need to modify the values in a seperate constants-class to keep them valid, although little if anything of interest in the xsd has changed. The xsd is maintained elsewhere, so just changing it is no option.
Thus I was asking myself wether there is a jaxb-plugin or similar to turn fixed-value attributes into constants ala
#XmlAttribute(name = "listVersionID")
#XmlJavaTypeAdapter(NormalizedStringAdapter.class)
#XmlSchemaType(name = "normalizedString")
protected final String listVersionID = "1.0";
instead of just
#XmlAttribute(name = "listVersionID")
#XmlJavaTypeAdapter(NormalizedStringAdapter.class)
#XmlSchemaType(name = "normalizedString")
protected String listVersionID;
which must be populated manually.
Does anyone know of such?
If you don't want to modify your schema, another option is to use an external binding file:
<?xml version="1.0" encoding="UTF-8"?>
<jaxb:bindings xmlns:jaxb="http://java.sun.com/xml/ns/jaxb"
version="2.0"
xmlns:xs="http://www.w3.org/2001/XMLSchema">
<jaxb:bindings schemaLocation="yourschema.xsd" node="/xs:schema">
<jaxb:globalBindings fixedAttributeAsConstantProperty="true" />
</jaxb:bindings>
</jaxb:bindings>
It is equivalent to what proposes #jmattheis in his answer.
Yes it is possible through custom jaxb bindings, which can be added as file at the codegen.
In the jaxb bindings, there is the fixedAttributeAsConstantProperty-attribute. Setting this to true, instructs the code generator to generate attributes with the fixed attribute as java-constants.
There are 2 options for this:
1. Through global bindings:
which then make all attributes with fixed values to constants
<schema targetNamespace="http://stackoverflow.com/example"
xmlns="http://www.w3.org/2001/XMLSchema"
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
xmlns:jaxb="http://java.sun.com/xml/ns/jaxb"
jaxb:version="2.0">
<annotation>
<appinfo>
<jaxb:globalBindings fixedAttributeAsConstantProperty="true" />
</appinfo>
</annotation>
...
</schema>
2. Through local mappings:
Which only defines the fixedAttributeAsConstantProperty property on a specific attribute.
<schema targetNamespace="http://stackoverflow.com/example"
xmlns="http://www.w3.org/2001/XMLSchema"
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
xmlns:jaxb="http://java.sun.com/xml/ns/jaxb"
jaxb:version="2.0">
<complexType name="example">
<attribute name="someconstant" type="xsd:int" fixed="42">
<annotation>
<appinfo>
<jaxb:property fixedAttributeAsConstantProperty="true" />
</appinfo>
</annotation>
</attribute>
</complexType>
...
</schema>
Both examples should result in:
#XmlRootElement(name = "example")
public class Example {
#XmlAttribute
public final static int SOMECONSTANT = 42;
}

JAXB/XJC external binding for renaming versus multiple XSD compile

(I've already googled it and done a search here and found no answer, maybe I'm using the wrong keywords...)
To make it simple, I have two schemas:
a.xsd:
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns="http://foo.bar/something"
targetNamespace="http://foo.bar/something"
elementFormDefault="qualified"
attributeFormDefault="unqualified">
<xs:complexType name="TFoo">
<xs:attribute name="version" type="xs:string" />
</xs:complexType>
</xs:schema>
b.xsd:
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns="http://foo.bar/something"
targetNamespace="http://foo.bar/something"
elementFormDefault="qualified"
attributeFormDefault="unqualified">
<xs:complexType name="TFoo">
<xs:attribute name="version" type="xs:string" />
<xs:attribute name="dateTime" type="xs:dateTime" />
</xs:complexType>
</xs:schema>
Both have the same targetNamespace and a complexType named TFoo.
I have an external binding to change the generated class name of a.xsd from TFoo to TFooA:
a-binding.xml:
<jxb:bindings
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:jxb="http://java.sun.com/xml/ns/jaxb"
xmlns:xjc="http://java.sun.com/xml/ns/jaxb/xjc"
version="2.1">
<jxb:bindings schemaLocation="a.xsd">
<jxb:bindings node="//xs:complexType[#name='TFoo']">
<jxb:class name="TFooA"/>
</jxb:bindings>
</jxb:bindings>
</jxb:bindings>
which works if I compile the a.xsd alone:
$ xjc -b a-binding.xml a.xsd
parsing a schema...
compiling a schema...
bar/foo/something/ObjectFactory.java
bar/foo/something/TFooA.java
bar/foo/something/package-info.java
(look how I got TFooA.java)
But, if I try to compile both schemas at once, I get:
$ xjc -b a-binding.xml a.xsd b.xsd
parsing a schema...
[ERROR] 'TFoo' is already defined
line 13 of file:/home/scherrer/tmp/PL_008f/b.xsd
[ERROR] (related to above error) the first definition appears here
line 9 of file:/home/scherrer/tmp/PL_008f/a.xsd
Failed to parse a schema.
I know TFoo is defined twice, and that's why I have the external binding to solve the conflict.
Obs. both schemas are fictitious, written to exemplify the problem, the real ones (many) are provided by a third party and I can't change them.
Can anyone tell me if this is some kind of xjc restriction (it's not listed here) or shouldn't work at all? Or maybe a bug?
Thanks in advance.
Having 2 different schema documents defining the same name space (and even worse - same element) is equivalent to having 2 different jars contain the same package and same class(es) in the package. This isn't a limitation of jaxb per se - it is a violation of what schema name spaces mean.
In short, you cannot process these schemas together.
The generator cannot create classes because it does not know what to reference. The failure occurs before your attempt to rename. It occurs when reading the schemas.
What you could do is process the schemas separately and change the java package names which are used. This avoids naming conflict in the java package space and basically treats the schema docs as completely separate entities with no references to each other. This can be done by defining the package name to use in the binding:
<jxb:bindings
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:jxb="http://java.sun.com/xml/ns/jaxb"
xmlns:xjc="http://java.sun.com/xml/ns/jaxb/xjc"
version="2.1">
<jxb:bindings schemaLocation="a.xsd" node="/xs:schema">
<jxb:schemaBindings>
<jxb:package name="com.foo.a"></jxb:package>
</jxb:schemaBindings>
</jxb:bindings>
</jxb:bindings>
I think you found a limitation of data binding. Try data projection instead (Disclosure: I'm affiliated with that project).
Your schema b.xsd looks just like an extension to the a.xsd. You can create a projection interface suitable for b.xsd and use it with any documents that fit either a.xsd or b.xsd.

Generating unique serializable id for each of the generated classes in JAXB

I am using ant wsimport to generate client stub from the wsdls. Also, I would like to generate client classes that implements Serializable. I would like to generate a different serialVersionUID for each class. I tried with the binding file that was shown below. But its generating same serialVersionUID for all the classes. Is there any way I can give my own serialVersionUID to each class?
<wsimport xendorsed="true" binding="binding.xml" debug="true" keep="true"
verbose="false" sourcedestdir="${generated}" wsdl="${src}${wsdl.file}"
wsdlLocation="${wsdl.file}">
</wsimport>
binding configuration
<bindings xmlns="http://java.sun.com/xml/ns/jaxb" version="2.0" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<globalBindings>
<serializable uid="1" />
</globalBindings>
</bindings>
Just for the record, there is no way to generate a unique serialVersionUID for each generated class because it doesn't make sense to do so.
Let me explain :
A serialVersionUID represents a version of your class at a particular point in time. If you modify your class, your serialVersionUID should change.
So when the JDK deserialize objects of the same class, it knows to which version of your class to deserialize it to.
In the case of JAXB, since you generate all your classes at once every time it doesn't make sense to version all the classes individually. Simply because they can only change as a group. (Unless you take them out of your target folder..)
I hope that makes a little bit more sense.
This is the binding file we use, which does the trick for us.
<xs:schema elementFormDefault="qualified" version="1.0"
xmlns:xs="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:version="2.0"
jaxb:extensionBindingPrefixes="xjc">
<xs:annotation>
<xs:appinfo>
<jaxb:globalBindings>
<xjc:serializable />
</jaxb:globalBindings>
</xs:appinfo>
</xs:annotation>

Provide schema for xsd:any element while importing WSDL using wsimport

I have a WSDL that uses an xsd:any element in a return type for one of the methods, like this:
<xs:element name="Method_XMLResponse">
<xs:complexType>
<xs:sequence>
<xs:element minOccurs="0" maxOccurs="1" name="Method_XMLResult">
<xs:complexType mixed="true">
<xs:sequence>
<xs:any/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
When I run the WSDL through the wsimport tool, I get a generated class that has this xs:any field mapped as a list of objects:
public static class MethodXMLResult {
#XmlMixed
#XmlAnyElement(lax = true)
protected List<Object> content;
}
When invoking the service using the generated code, I get instances of org.w3c.dom.Node in the content list (com.sun.org.apache.xerces.internal.dom.ElementNSImpl to be precise) that I would need to parse myself. I was, however, provided with a separate, external schema document for the objects actually returned - and I'm trying to somehow feed it to wsimport so it generates the classes for them as well.
I'm trying to accomplish that through JAX-WS / JAXB customization file like this:
<jaxws:bindings xmlns:s="http://www.w3.org/2001/XMLSchema"
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
xmlns:xjc="http://java.sun.com/xml/ns/jaxb/xjc"
xmlns:jaxws="http://java.sun.com/xml/ns/jaxws" xmlns:a="http://www.w3.org/2001/XMLSchema"
wsdlLocation="wsdlLocation.wsdl">
<jaxws:bindings node="wsdl:definitions">
<jaxws:bindings node="wsdl:types" xmlns:jaxb="http://java.sun.com/xml/ns/jaxb">
<jaxws:bindings node="//s:schema[#targetNamespace='wsNamespace']">
<jaxb:bindings node="//s:element[#name='Method_XMLResponse']//s:any">
...
</jaxb:bindings>
</jaxws:bindings>
</jaxws:bindings>
</jaxws:bindings>
</jaxws:bindings>
Looks like wsimport picks the right location to customize (gave me numerous error meesages with properly designated line number in the WSDL), but I can't figure out how to fill the <jaxb:bindings> element to make wsimport generate classes from the external schema. Is it even possible? Any help would be much appreciated.
I see you are use mixed type with xs:any in your XSD. I think it usefull custimization for a mixed type following JAXB adjust:
<?xml version="1.0" encoding="UTF-8"?>
<jaxb:bindings
xmlns:jaxb="http://java.sun.com/xml/ns/jaxb" jaxb:version="2.0"
xmlns:xjc= "http://java.sun.com/xml/ns/jaxb/xjc" jaxb:extensionBindingPrefixes="xjc">
<jaxb:globalBindings generateMixedExtensions="true"/>
</jaxb:bindings>
You can add external JAXB binding files to wsimport whith -b parameter.
I think you can adjust xs:any with following ways:
Skip shema:
<xs:any processContents="skip" maxOccurs="unbounded" minOccurs="0" />
Skip binding:
#XmlAnyElement
public List<Element> getAny();
Strict schema:
<xs:any maxOccurs="unbounded" minOccurs="0" />
Strict binding:
#XmlAnyElement(lax=true)
public List<Object> getAny();
and:
with processContents=lax means any XML elements can be
placed here, but if their element names match those defined in the
schema, they have to be valid. XJC actually handles this exactly like
processContents='strict', since the strict binding allows unknown
elements anyway.
You can read more about in this link.
May be help this answer to accomplish your JAX-WS / JAXB customization file.

change package of generated jaxb class

I have the following data type defined in a wsdl:
<?xml version="1.0" encoding="UTF-8"?>
<wsdl:definitions name="myService" targetNamespace="http://example.com" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:tns="http://example.com" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/">
<wsdl:types>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:tns="example.com" targetNamespace="example.com" version="1.0">
<xs:simpleType name="MyEnum">
<xs:restriction base="xs:string">
<xs:enumeration value="one"/>
<xs:enumeration value="two"/>
</xs:restriction>
</xs:simpleType>
<!-- SNIP other data types -->
</xs:schema>
</wsdl:types>
</wsdl:definitions>
I want MyEnum to fall into its own package. So, I used a jaxws binding file, and used XPATH to traverse the schema and jaxb bindings to set the package, as follows:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<jaxws:bindings xmlns:jxb="http://java.sun.com/xml/ns/jaxb" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns="http://java.sun.com/xml/ns/jaxws">
<jaxws:bindings node="wsdl:definitions/wsdl:types/xs:schema[#targetNamespace='example.com']">
<jxb:schemaBindings>
<jxb:package name="abra.ca.dabra" /> <!-- this works, and changes package of all classes in the namespace-->
</jxb:schemaBindings>
<jxb:bindings node="//xs:simpleType[#name='MyEnum']">
<jxb:package name="a.b.c"/> <!-- this does not work -->
</jxb:bindings>
</jaxws:bindings>
<!-- SNIP - other functional jaxws bindings -->
</jaxws:bindings>
Now, the path set in schemaBindings takes - and puts all data types from that schema/targetnamespace into package abra.ca.dabra. However, I can't seem to set the package of just MyEnum - which is what I want.
I use cxf's wsdl2java to provide the bindings file. Am I missing something?
I would try it with something likethis:
<jaxws:bindings xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
xmlns:jaxb="http://java.sun.com/xml/ns/jaxb"
xmlns:jaxws="http://java.sun.com/xml/ns/jaxws">
<jaxws:enableWrapperStyle>false</jaxws:enableWrapperStyle>
<!-- set default package structure -->
<jaxws:package name="abra.ca.dabra" />
<!-- set package structure for complex schema types -->
<jaxws:bindings node="wsdl:definitions/wsdl:types/xs:schema/xs:simpleType[#name='MyEnum']">
<jaxb:schemaBindings>
<jaxb:package name="a.b.c" />
</jaxb:schemaBindings>
</jaxws:bindings>
To be more precise you should make the whole wsdl available
Hope this helps ...
Have you tried to add a second schema to the WSDL that only contains MyEnum, with a different namespace? The original schema definition should then import that type from the new namespace.

Categories