Customize object/element name with JAXB - java

I'm very new to JAXB, so I'm having trouble cracking this (I assume) very simple use case.
I have a set of schemas I got. I have no control over those, I cannot change them. In these schemas, I have declarations such as
<xs:complexType name="CustomerType">
...
I try to generate classes from these. So such a declaration becomes
#XmlType(name = "CustomerType", propOrder = {
"field1",
"field2"
})
public class CustomerType {
...
Then I need to use this class to create XML messages using a RestTemplate. The problem is, the object in the XML message is not supposed to be "CustomerType", it's supposed to be just "Customer". Like I said, I cannot edit the schemas. I also cannot directly edit the generated sources. I need some kind of external customization that tells either the source generating process, or the marshalling process, how to transform the names of the objects. Any advice will be greatly appreciated.

You can use bindings to customize class or property names. Typically you'll have a file like bindings.xjb like this:
<jaxb:bindings version="1.0" 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:extensionBindingPrefixes="xjc">
<jaxb:bindings schemaLocation="schema.xsd" node="/xsd:schema">
<jaxb:bindings node="xsd:customType[#name='CustomerType']">
<jaxb:class name="Customer"/>
</jaxb:bindings>
<jaxb:bindings node="xsd:customType[#name='CustomerType']//xsd:element[#name='field1']">
<jaxb:property name="f1"/>
</jaxb:bindings>
</jaxb:bindings>
</jaxb:bindings>
There are quite a few things you can customize with bindings (see this), but certainly not everything.

The way DTO objects are created is:
You define all the types at one place like: CustomerType, UserType, OrderType and so on.
You then create another schema and import appropriates XSD's within where you have defined multiple types. Then you actually use/aggregate these type definitions to create your XML Schema for objects . Something like below
<xs:complexType name="CustomerOrders">
<xs:element name="Customer" type="CustomerType">
<xs:attribute name="Orders" type="OrderType">

Related

JAXB binding to annotate all strings in all schemas under /schema with a custom annotation

I have a dir /schemas that has many .xsd files, and I want to annotate every field of type String with a custom annotation I created.
I'm using this plugin: "org.jvnet.jaxb2_commons:jaxb2-basics-annotate:0.6.4".
I think creating a jaxb binding config would be my ideal solution. I tried this:
<jaxb:bindings>
<jaxb:bindings node="xs:element[#type='xs:string']">
<annox:annotateClass>#com.myapp.model.CustomAnnotation</annox:annotateClass>
</jaxb:bindings>
</jaxb:bindings>
But in vain. Can you help? Thank you

XML & JAXB: pass attribute into value

I have a large amount of objects generated through JAXB (maven-jaxb2-plugin) and annotate them with the jaxb2-annotate-plugin. These classes may define a RelationType and I'd like to annotate them with the corresponding #RelationType annotation. I use an XPath expression to find the name attribute in the XSD and annotate the class, passing its specific type into the annotation. An example of this is the following:
<jaxb:bindings node="//xsd:complexType[#name='SomeRelationType']">
<annox:annotate target="class">#com.example.RelationType(type = "SomeRelationType")</annox:annotate>
</jaxb:bindings>
which maps on the following XSD snippet:
<xsd:complexType name="SomeRelationType">
<xsd:complexContent>
<xsd:extension base="RelationType">
<xsd:sequence>
<xsd:element name="someValue" type="SomeValue"/>
<xsd:element name="otherValue" type="OtherValue"/>
</xsd:sequence>
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
I find the ComplexType with the SomeRelationType name and annotate the class with a #RelationType annotation, which has the SomeRelationType as its type parameter. It would generate the following class:
#RelationType(type = "SomeRelationType")
public class SomeRelationType extends RelationType implements Serializable {
private final static long serialVersionUID = 1L;
protected SomeValue someValue;
protected OtherValue otherValue;
}
This works fine if it were just a few domain objects. But I have a large amount and defining every annotation manually is not only tedious but also bad in terms of change and expansion.
To generify it, I can rewrite the XPath expression to the following:
<jaxb:bindings node="//xsd:complexType[substring(#name, string-length(#name) - string-length('RelationType') + 1)]" multiple="true">
<annox:annotate target="class">#com.example.RelationType(type = "SomeRelationType")</annox:annotate>
</jaxb:bindings>
The problem: The type parameter of my annotation is still defined as "SomeRelationType". It would be great if I could use the same #name as defined in the XPath expression. Then all the classes whose name ends with "RelationType" also automatically gets its #RelationType annotation with the correct type parameter.
It doesn't work as simple as doing the following of course, but it shows what I'd like to achieve:
<jaxb:bindings node="//xsd:complexType[substring(#name, string-length(#name) - string-length('RelationType') + 1)]" multiple="true">
<annox:annotate target="class">#com.example.RelationType(type = #name)</annox:annotate>
</jaxb:bindings>
Is such a thing even possible or is this impossible in XML/JAXB?
But I have a large amount and defining every annotation manually is not only tedious but also bad in terms of change and expansion.
For me, #com.example.RelationType(type = "SomeRelationType") looks like a trivial metainformation which could be derived via reflection without any annotations. So check if there's a way to do "convention over configuration" thing.
The jaxb2-annotate-plugin does not and will not support parameterization, it would be too narrow and too complex to implement. Disclaimer I'm the author of the jaxb2-annotate-plugin.
I see two options:
Pre-generate bindings in your build. XML Schema is XML so writing XSLT to produce bindings files should not be too complicated.
Write an own XJC plugin to add annotations as you want them.
Bribe me to add parameterization to the jaxb2-annotate-plugin.
Right, just two options.

spring ws soap client+jaxb2

I am using the JAXb2 maven plugin to create the domain[bean ]classes.
When i try to hit the service thru WebServiceTemplate class, I am getting below error.
Exception:org.springframework.oxm.UnmarshallingFailureException: JAXB unmarshalling exception; nested exception is javax.xml.bind.UnmarshalException: unexpected element (uri:"http://services.abc.cde.efg.com", local:"xxxLookupResponse"). Expected elements are <{services.abc.cde.efg.com"}xxxLookupRequest>,<{services.abc.cde.efg.com"}xxxLookupResponse>,<{services.abc.cde.efg.com"}zzzUploadOrder>
and my bindings file looks as follows.
I am wondering how is it expecting three elements, that too the last one is not at all related with this webservice and not defined in jaxb binding file also. I have checked the created bean classes and they are not interrelated.
<jaxb:bindings schemaLocation="abcsServices.xsd" node="/xs:schema">
<jaxb:bindings node="xs:complexType[#name='xxxxxLookupRequest']">
<annox:annotate>
<annox:annotate annox:class="javax.xml.bind.annotation.XmlRootElement"
name="xxxxxxLookupRequest" />
</annox:annotate>
</jaxb:bindings>
</jaxb:bindings>
<jaxb:bindings schemaLocation="abcsServices.xsd" node="/xs:schema">
<jaxb:bindings node="xs:complexType[#name='xxxxxLookupResponse']">
<annox:annotate>
<annox:annotate annox:class="javax.xml.bind.annotation.XmlRootElement"
name="xxxxxLookupResponse" />
</annox:annotate>
</jaxb:bindings>
</jaxb:bindings>
Please throw some light on this. I am stuck here.
Client code:
ObjectFactory objFactory = new ObjectFactory(); xxxxLookupRequest req = objFactory.createxxxxLookupRequest(); req.setxxxx("12344"); req.setxxxxx("34234"); xxxxxLookupResponse response = (xxxxxLookupResponse) xxxxClient .doDeliveryLookUp(req);
========
spring client code:
response = (xxxxxLookupResponse) getWebServiceTemplate() .marshalSendAndReceive(xxxxLookupRequest, new SoapActionCallback("urn:xxxxxLookup"));
How do you create your JAXBContext? Please show the Spring configuration.
The error message is also weird:
...unexpected element (uri:"http://services.abc.cde.efg.com", local:"xxxLookupResponse"). Expected elements are ... <{services.abc.cde.efg.com"}xxxxxLookupResponse>...
Do you have matching names (xxxLookupResponse vs. xxxxxLookupResponse) here?
My theory is that you have one package (services.abc.cde.efg.com) generated or defined in two places and JAXB is picking the wrong one.
The binding files you refer to are not directly considered by JAXB, they are only used during the code generation. JAXB works with the generated classes then.

JAXB binding multiple files with same namespace to same package

I'm having a schema hierarchy like this:
common
|---legacy
| |---legacy.xsd xmlns="http://common/legacy"
| |---other.xsd xmlns="http://common/legacy"
| '---....xsd xmlns="http://common/legacy"
|---send
|---file.xsd xmlns="http://common/send"
'---text.xsd xmlns="http://common/send"
'---....xsd xmlns="http://common/send"
All files in one folder have the same namespace.
Now I want to map the namespaces to specific java packages (I cannot change the namespace).
I found a solution to bind a schema to a package. But then I would have to create one entry per xsd-file:
<jaxb:bindings schemaLocation="./common/legacy/legacy.xsd">
<jaxb:schemaBindings>
<jaxb:package name="com.company/legacy"/>
</jaxb:schemaBindings>
</jaxb:bindings>
<jaxb:bindings schemaLocation="./common/legacy/other.xsd">
<jaxb:schemaBindings>
<jaxb:package name="com.company/legacy"/>
</jaxb:schemaBindings>
</jaxb:bindings>
.....
Is there a way to directly define a binding between namespace and a package name?
The other way would be to define the package in maven:
<plugin>
<groupId>org.jvnet.jaxb2.maven2</groupId>
<artifactId>maven-jaxb2-plugin</artifactId>
<configuration>
<generatePackage>com.company/legacy</generatePackage>
</configuration>
</plugin>
But then I would have to create one execution per folder, which is not really what I want.
Disclaimer: I'm the author of maven-jaxb2-plugin.
XJC derives packages from namespaces, so you (normally) can't generate several packages for one namespace. There are a few tricks with jaxb:class/#ref but you don't want those as this may lead to all kinds of collisions.
So my suggestion would be to define multiple executions, one per distinct schema in the same namespace. You could use generatePackage although I generally advise to define package mappings in bindings instead.
When doing multiple executions, make sure you use distinct generateDirectory per execution.
By the way, why are you not comfortable with multiple bindings?
Use scd syntax instead node="XPath" schemaLocation="path or *":
<jaxb:bindings scd="x-schema::tns" xmlns:tns="http://common/legacy">
<jaxb:schemaBindings>
<jaxb:package name="com.company.legacy"/>
</jaxb:schemaBindings>
</jaxb:bindings>
SCD is not supported everywhere. Some XJC Customizations, like the jaxb2-basics, not working in jaxb:bindings.
JAXB RI 2.1 User guide
Schema Component Designator - recommendation

JAXB javaType customization on xs:integer produces #XmlElement with "type=String.class"

When generating Java beans from a XSD with XJC, I need to map xs:integer to Integer rather than BigInteger. I added a javaType tag to my JAXB customization file (as said in many answers from this site), and it worked fine.
But in the generated code I noticed that the #XmlElement tag now has a type=String.class parameter.
So now I wonder, why String?
Is it because the parse and print methods are converting from/to string objects?
I tried with xjc:javaType instead of jaxb:javaType, allowing me to replace the generated Adapter1<String, Integer> with a custom MyAdapter<BigInteger, Integer>, but exactly the same thing happened.
If this is normal XJC behavior, is it possible to tweak it to generate code without this parameter, or with another value than String?
Note that everything is working fine, but I would like to understand.
Also I'm using Enunciate to document my API and it seems to be confused by this type thing (but this is probably a bug in Enunciate).
I'm using JAXB RI 2.2.6, and here are some pieces of code, to illustrate my question:
bindings.xjb
<jaxb:bindings version="2.0" ...>
<jaxb:globalBindings>
<jaxb:javaType
name="java.lang.Integer"
xmlType="xs:integer"
parseMethod="..."
printMethod="..." />
</jaxb:globalBindings>
</jaxb:bindings>
Field definition in the XSD
<xs:complexType name="MyType">
<xs:sequence>
<xs:element name="myField" type="xs:integer" />
</xs:sequence>
</xs:complexType>
Generated Java field
#XmlElement(namespace = "...", required = true, type = String.class)
#XmlJavaTypeAdapter(Adapter1.class)
#XmlSchemaType(name = "integer")
protected Integer myField;
I know this is an old question, but for the people still looking for an answer:
using type xs:int instead of xs:integer will create a normal java int instead of the Biginteger.

Categories

Resources