XML & JAXB: pass attribute into value - java

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.

Related

Generate wrapper for the Jaxb generated ObjectFactory methods nillable=true, minOccurs=0

I am using JAXB, I have xsd files which I cannot modify which has elements with
minOccurs="0" nillable="true"
Which results that in the generated ObjectFactory class I have a lot of code like:
JAXBElement<SomeObject>
Then the code, that I have to write to both marshall and unmarshall is boilerplate and ugly.
Is there a way to generate somehow automaticaly another layer of abstraction so that I would have some other ObjectFactory class as well, but it would not operate on JAXBElement but instead would have it wrapped in some methods? Like it is when in the binding.xjb you specify the following:
<jaxb:globalBindings localScoping="toplevel" generateElementProperty="false">
which is unfortunately not an option for me.

Customize object/element name with JAXB

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">

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.

Adding annotations to JAXB-generated classes which depend on information in XSD

I have a WSDL + XSD that needs to be turned into Java classes. That's pretty simple - wsimport will handle that without issue. However, I also need to be able to add annotations to the generated classes, and those annotations need to contain information that is contained in the XSD (in particular, they need to reference the xsd:maxLength or xsd:length properties).
Why? Because I plan to transform them into a flat file afterwards, using Bindy. For reference, I know that I can use Annox to add custom annotations to the generated classes, but as far as I'm aware, that would require that either all annotations are identical, with all parameters being identical, or specifying annotations for each element individually, with no way to specify the annotation once along with some way (e.g. xpath) of specifying that the value of one of the parameters should be different for each element.
That is, given a schema extract like
<xsd:element name="smapleRequest">
<xsd:sequence>
<xsd:element name="ELEMENT_ONE">
<xsd:simpleType>
<xsd:restriction base="xsd:string">
<xsd:length value="3" />
</xsd:restriction>
</xsd:simpleType>
</xsd:element>
<xsd:element name="ELEMENT_TWO">
<xsd:simpleType>
<xsd:restriction base="xsd:string">
<xsd:maxLength value="8" />
</xsd:restriction>
</xsd:simpleType>
</xsd:element>
</xsd:sequence>
</xsd:element>
I would like to see classes that look this:
.
.
.
#FixedLengthRecord
public class SampleRequest {
#XmlElement(name = "ELEMENT_ONE", required = true)
#DataField(pos = 1, length=3)
protected String elementOne;
#XmlElement(name = "ELEMENT_TWO", required = true)
#DataField(pos = 4, length=8)
protected String elementTwo;
.
.
.
}
Ideally, I would like to be able to do this without having to duplicate all the information from the XSD into the JAXB Binding File. I mean, I could, but with potentially hundreds of elements per web service method, and dozens of methods, that would get very, very old very, very fast. At that point, I would probably have to use another tool to generate the XSD and JAXB binding file(s) from the COBOL!
So, does anyone know if this is possible? Have I just missed something in Annox? Or am I just asking for too much here?
You have few options: XJC plugins is one route and Annox looks interesting. But I'm no expert so I'll let others explore it with you.
The other route I would suggest you consider, if you get stuck with the first one, is to post-process your generated JAXB sources via annotation processing (formerly the apt tool, now part of the javac tool) to access the XSD and append your annotations on the fly. Not sure that would work for all your cases, but in the example you gave, the JAXB-generated annotations should be enough to construct an XPath expression to read the corresponding XML element type characteristics. Assuming your needs are essentially around the field length, that should be few use cases and XPath expressions.
To automatically add XJsr303Annotations annotations you could use xjc plugin https://github.com/krasa/krasa-jaxb-tools
Plese see my answer Generation of XSD restrictions in a schema generated from Java JAXB annotated classes for details.

How can I add WSDL <types> restrictions programmatically?

Where I work, we have a policy in place that states we should try to build web services "bottom-up" (code-first).
When doing this, how can I add restrictions within my XSD <types> WSDL element? For example, in the following:
import javax.jws.WebService;
import javax.xml.ws.BindingType;
#WebService
#BindingType(javax.xml.ws.soap.SOAPBinding.SOAP12HTTP_BINDING)
public class ProofOfConcept {
public String sayHello(String guest){
return "Hello "+guest;
}
}
The output in my WSDL is as such:
...
<xs:complexType name="sayHello">
<xs:sequence>
<xs:element minOccurs="0" name="arg0" type="xs:string" />
</xs:sequence>
</xs:complexType>
...
I would like to add both minLength and maxLength restrictions to the "guest" String. Is this possible to do through code as opposed to editing the WSDL directly?
I'm aware that I can use a custom class and annotate my fields with #XmlElement or #XmlAttribute to get a few customizations (name, required etc.), but nothing specific for something like a String (length etc.); is it possible to modify / extend those annotations to add support for something like length or pattern?
Thanks.
I find it interesting that your policy is to try to build Web services bottom-up as I recommend building Web services top-down in order to cleanly define the contract. At any rate...
I'm not aware of a specific solution using annotations and I see that you haven't found one in your search either. If you want to automate the process so as to avoid overwriting your changes to the schema every time you regenerate the WSDL you can add an XSL transform to the resulting WSDL to add these attributes. While not an ideal solution it should allow you to set the attributes and continue working.

Categories

Resources