How to make JAXB unmarshaller use getters rather than fields - jaxb

I have some classes generated automatically using maven-jaxb2-plugin and jaxbfx. The latter generates JAXB classes with getters and setters that should be called when marshalling and unmarshalling respectively. However, the JAXB marshaller and unmarshaller methods use fields instead of the getters and setters.
The following code shows an example of a class generated with jaxbfx.
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "MyType")
public class MyType{
#XmlAttribute(name = "id", required = true)
protected int id;
private final transient IntegerProperty idProxy = new SimpleIntegerProperty();
public void setId(int value) {
this.id = value;
this.idProxy.set(value);
}
public short getId() {
return this.idProxy.get();
}
public IntegerProperty idProperty() {
return this.idProxy;
}
}
Therefore, I would like to know whether it is possible to make the marshaller and unmarshaller use the getters and setters instead of the fields. Please note that I cannot change the JAXB annotations manually, since they are generated automatically.

A working solution follows:
Modify the jaxbfx to clear all JAXB annotations from generated classes: this is achieved by invoking method clearAllAnnotations(implClass) for every class to be generated
Install the the jaxb2-basics-annotate plugin.
Modify the xml schema (file.xsd) to add the #XmlElement annotation to any getter and setter method.
For example:
<xs:complexType name="MyType">
<xs:attribute name="id" type="xs:int" use="required">
<xs:annotation>
<xs:appinfo>
<annox:annotate target="setter">#javax.xml.bind.annotation.XmlAttribute(required=true,name="id")
</annox:annotate>
</xs:appinfo>
</xs:annotation>
</xs:attribute>
</xs:complexType>

Related

JAXB generating unwanted #XMLValue

Been grappling with a problem for about half a week now, and need help as I cannot seem to make any headway.
I've inherited an application which hasn't been treated nicely for 8 years....still on Java 1.4, Maven1 build, no new unit tests for 8 years...
Currently the upgrade to Java 1.6 (Java 1.8 branch also done in parallel, will test both) and Maven 3.3.3 is well in swing - have been making excellent headway.
Now I've hit a wall and not made a breakthrough for a while.
The old sources used local JAXB 1.3 jars to generate classes from a large XSD.
I had to migrate from JAXB1.3 to JAXB2.1 - which also meant i had to spend a lot of time rewriting all the references to the generated classes as the naming conventions changed.
Anyway, a lot of time was spent getting the code to compile.
Finally, it compiles, and I try out a unit test to see how it works.
This is where i hit my problem.
Most of the classes compiled work fine, but three of the packages throw exceptions when i try to generate the JAXBContext:
#XmlValue is not allowed on a class that derives another class.
I've narrowed the problem down to a pattern which occurs in a couple of the generated classes.
The class that causes the exception is defined in the schema as below:
<xs:element name="ContactName">
<xs:complexType>
<xs:simpleContent>
<xs:extension base="xs:string">
<xs:attribute name="First" type="xs:string"/>
<xs:attribute name="Middle" type="xs:string"/>
<xs:attribute name="Last" type="xs:string"/>
<xs:attribute name="Name" type="xs:string"/>
</xs:extension>
</xs:simpleContent>
</xs:complexType>
</xs:element>
and then this element is referenced in another as follows:
<xs:element name="ContactInfo">
<xs:complexType>
<xs:annotation>
<xs:documentation>Common contact information</xs:documentation>
</xs:annotation>
<xs:sequence>
<xs:element ref="ContactName" minOccurs="0" maxOccurs="unbounded"/>
<xs:element ref="ContactID" minOccurs="0"/>
<xs:element ref="ContactDivision" minOccurs="0" maxOccurs="unbounded"/>
.....
this is generated into:
ContactName:
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "", propOrder = {
"value"
})
#XmlRootElement(name = "ContactName")
public class ContactName
extends BaseJaxbDoc
implements Serializable, Cloneable, CopyTo
{
private final static long serialVersionUID = 47110815L;
#XmlValue
protected String value;
#XmlAttribute(name = "First")
protected String first;
#XmlAttribute(name = "Middle")
protected String middle;
#XmlAttribute(name = "Last")
protected String last;
#XmlAttribute(name = "Name")
protected String name;
And then declared in ContactInfo as follows:
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "", propOrder = {
"contactName",
"contactID",
"contactDivision",
"contactPhone",
"contactPhoneHome",
"contactPhoneMobile",
"contactFax",
"contactEmail",
"contactEmail2"
})
#XmlRootElement(name = "ContactInfo")
public class ContactInfo
extends BaseJaxbDoc
implements Serializable, Cloneable, CopyTo
{
private final static long serialVersionUID = 47110815L;
#XmlElement(name = "ContactName")
protected List<ContactName> contactName;
The exception thrown is at:
this problem is related to the following location:
at protected java.lang.String xxx.xx.xxxx.xxxx.orders.jaxb.ContactName.value
at xxx.xx.xxxx.xxxx.orders.jaxb.ContactName
at protected java.util.List xxx.xx.xxxx.xxxx.orders.jaxb.ContactInfo.contactName
at xxx.xx.xxxx.xxxx.orders.jaxb.ContactInfo
at protected java.util.List xxx.xx.xxxx.xxxx.orders.jaxb.CustomerReference.contactInfo
at xxx.xx.xxxx.xxxx.orders.jaxb.CustomerReference
at protected java.util.List xxx.xx.xxxx.xxxx.orders.jaxb.Item.customerReference
at xxx.xx.xxxx.xxxx.orders.jaxb.Item
at public xxx.xx.xxxx.xxxx.orders.jaxb.Item xxx.xx.xxxx.xxxx.orders.jaxb.ObjectFactory.createItem()
at xxx.xx.xxxx.xxxx.orders.jaxb.ObjectFactory
There is an XML transformation on the original schema, stripping comments out and creating jaxb:typesafeEnum types. Then the transformed schema is used with a jxb binding file to bind everything to an internal jaxb helper superclass - BaseJaxbDoc
<jaxb:globalBindings generateIsSetMethod="true">
<xjc:serializable uid="47110815"/>
<xjc:superClass name="xxx.xx.xxxx.xxxx.helpers.BaseJaxbDoc"/>
<jaxb:javaType name="java.math.BigDecimal" xmlType="xs:decimal"
parseMethod="xxx.xx.xxxx.xxxx.helpers.AmountConverter.parseAmount"
printMethod="xxx.xx.xxxx.xxxx.helpers.AmountConverter.printAmount"/>
</jaxb:globalBindings>
This is because I am using xjc on 9 different schemas, all generating JAXB packages of classes.
The classes all have the same superclass (defined in a bindings file for each schema) to only implement the JAXB marshall/unmarshall classes once, along with some other helper functions.
So my question is how to get around this exception when i cannot modify the schema?
Something in the XSLT or something in the bindings file?
My Maven dependencies:
for JAXB:
org.jvnet.jaxb2.maven2
maven-jaxb21-plugin
0.13.0
JAXB runtime:
org.glassfish.jaxb
jaxb-runtime
2.2.11
Try annotating BaseJaxbDoc with #XmlTransient.
The problem you're getting is produced here:
if(getBaseClass()!=null) {
builder.reportError(new IllegalAnnotationException(
Messages.XMLVALUE_IN_DERIVED_TYPE.format(), p ));
}
JAXB thinks your BaseJaxbDoc is a base class. So you should either remove xjc:superClass or trink JAXB into thinking that your class does not have a base class.
When I look at this part of the code in the ModelBuilder:
if(reader.hasClassAnnotation(clazz,XmlTransient.class) || isReplaced) {
// handle it as if the base class was specified
r = getClassInfo( nav.getSuperClass(clazz), searchForSuperClass,
new ClassLocatable<C>(upstream,clazz,nav) );
}
It seems that the ModelBuilder recognizes #XmlTransient on classes and does not consider them. So there's a chance that assing #XmlTransient on your BaseJaxbDoc would help.
Another option is to drop BaseJaxbDoc construct. You use class inheritance to add marshal/unmarshal functionality to the schema-derived classes. I'd rather move this functionality out into some external services. This is probably not an option here as you're probably facing a lot of legacy code.
A further option is to try MOXy instead of JAXB RI in the runtime.

Mapper library for xml to java objects

We are creating jaxb classes for a predefined schema. The schema contains certain elements which uses xs:choice to create complexTypes. In this case the binding being generated contain a List which makes it complex as we have to identify the actual instance and then cast it. We tried using the binding customization attribute "choiceContentProperty="false"" to change this behavior. But this does not seem to work. Any suggestions to override this behavior?
Disclaimer: I am the author of jaxb2-simplify-plugin.
This is a use case for the jaxb2-simplify-plugin.
This:
<xs:complexType name="typeWithElementsProperty">
<xs:choice maxOccurs="unbounded">
<xs:element name="foo" type="xs:string"/>
<xs:element name="bar" type="xs:int"/>
</xs:choice>
</xs:complexType>
Normally generates this:
#XmlElements({
#XmlElement(name = "foo", type = String.class)
#XmlElement(name = "bar", type = Integer.class),
})
protected List<Serializable> fooOrBar;
But with jaxb2-simplify-plugin you'll get this:
#XmlElement(name = "foo", type = String.class)
protected List<String> foo;
#XmlElement(name = "bar", type = Integer.class)
protected List<Integer> bar;

How to tell jaxb to truncate a BigDecimal when marshalling to xml?

I have these two classes:
#XmlAccessorType(XmlAccessType.FIELD)
public class OpportunityLocation
{
#XmlElement
private LatitudeLongitude coordinates;
[...]
}
public class LatitudeLongitude implements Serializable
{
private BigDecimal latitude;
private BigDecimal longitude;
[...]
}
when OpportunityLocation is serialized I get
<location>
[...]
<coordinates>
<latitude>51.53684899999999657893567928113043308258056640625</latitude>
<longitude>-0.1325880000000000114024345521102077327668666839599609375</longitude>
</coordinates>
Now I've been asked to provide an xsd to validate the xml, I set the type of latitude and longitude to xsd:decimal but the validator complains that
Element 'latitude': '51.53684899999999657893567928113043308258056640625' is not a valid value of the atomic type 'xs:decimal'.
(same for longitude)
The xsd fragment describing the coordinates is
<xs:complexType name="latitudeLongitude">
<xs:all>
<xs:element name="latitude" type="xs:decimal"/>
<xs:element name="longitude" type="xs:decimal"/>
</xs:all>
</xs:complexType>
I think the best solution would be to truncate the lat/long values, as there is no point to have that much precision in the first place.
How do I instruct JAXB to do that?
Or simply use BigDecimal.setScale(). No need for an XmlAdapter if applicable to your case.
BigDecimal bd = new BigDecimal((double) 1.2345);
bd = bd.setScale(2, RoundingMode.HALF_UP);
You can do this using an XmlAdapter. Below is a similar example demonstrating the concept with the Joda-Time classes:
http://blog.bdoughan.com/2011/05/jaxb-and-joda-time-dates-and-times.html

JAXB and validation against parts of a schema with schemagen?

A very generalized and simplified scenario description:
Application builds JAXB instances through UI,
The JAXB instances are built in different steps are composed in the end,
As this is done, I must perform validation of the JAXB instances created in each step against the XSD to verify that the UI tier does not build nonsense.
Now the schema defines complex types A, B and C. The schema specifies that XML document must have top level element A and this can contain multiple B and this can optionally include C. I create JAXB instance for B and absolutely want to validate this against the complex type definition in the XSD before nesting it under A. However validation of this will fail if I validate against the entire XSD.
Questions: How to validate JAXB instance against only a part of the XSD from which its class has been generated from?
How about using schemagen to generate schema from the JAXB class which instance I want to validate and then validate against that? Do you think that can work? Any other ideas?
I have no previous experience with schemagen and will start prototyping of this solution soon.
Note: in reality, the schemas are not as simple as in the above example and the solution of creating some always-valid mock of A is not feasible option. Not to mention that this kind of validation will be on hundred places to say the least.
create separate schema for element you want to validate, where this element is on root level.
to workaround missing #xmlRoottag see 101 ways to marshal objects with JAXB
Well it turns out that using xsi:type let's one accomplish this.
http://www.w3.org/TR/xmlschema-1/#xsi_type
While working with xjc and schemagen tools please consider same concept which we use in java. In java, every class is a skeleton and object is an instance. Same as we need to consider XSD as skeleton and XML as instance.
xjc tool:- Xsd to Java Class Or Unmarshaling
Consider the below XSD with namespaces for example. The xjc tool will generate the java class along with package-info and object-factory.
<?xml version="1.0" encoding="utf-8"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:ir="http://irctc.org/service" targetNamespace="http://irctc.org/service">
<xsd:element name="Customer" type="ir:CustomerType"/>
<xsd:complexType name="CustomerType">
<xsd:sequence>
<xsd:element name="name" type="xsd:string"/>
<xsd:element name="src" type="xsd:string"/>
<xsd:element name="dest" type="xsd:string"/>
<xsd:element name="price" type="xsd:float" minOccurs="0"/>
</xsd:sequence>
</xsd:complexType>
</xsd:schema>
Use below command
D:\>xjc Customer.xsd
parsing a schema...
compiling a schema...
org\irctc\service\CustomerType.java
org\irctc\service\ObjectFactory.java
org\irctc\service\package-info.java
Note :- If you have multiple java class then you can use jaxb.index file instead of using ObjectFactory.java.
schemagen tool:- Java Class to Xsd Or Marshaling
Let us suppose we want to generate the Xsd file using java class then first we need to create the ObjectFactory.java Or jaxb.index file and package-info.java in the same package where we have the CustomerType.java.
package org.irctc.service;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;
#XmlRootElement("Customer")
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "CustomerType", propOrder = {
"name",
"src",
"dest",
"price"
})
public class CustomerType {
#XmlElement(required = true)
protected String name;
#XmlElement(required = true)
protected String src;
#XmlElement(required = true)
protected String dest;
protected Float price;
public String getName() {
return name;
}
public void setName(String value) {
this.name = value;
}
public String getSrc() {
return src;
}
public void setSrc(String value) {
this.src = value;
}
public String getDest() {
return dest;
}
public void setDest(String value) {
this.dest = value;
}
public Float getPrice() {
return price;
}
public void setPrice(Float value) {
this.price = value;
}
}
Use below command
D:\>schemagen org.irctc.service.CustomerType
Note: Writing D:\schema1.xsd
The above command will generate the xsd file. The package of java class will considered as xsd namespaces.
The above details are only for understanding marshaling and unmarshaling process using tools in Jax-B API.
For more details , check below examples
Jax-B Hello World Example
Java-XML mapping with Jax-B 2.0

JAXB - XJC - influencing generated typesafe enum class and members

When compiling the following simpleType with the XJC compile (from the JAXB package)...
<xs:simpleType name="test">
<xs:annotation>
<xs:appinfo>
<jaxb:typesafeEnumClass/>
</xs:appinfo>
</xs:annotation>
<xs:restriction base="xs:string">
<xs:enumeration value="4">
<xs:annotation>
<xs:appinfo>
<jaxb:typesafeEnumMember name="FOUR"/>
</xs:appinfo>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value="6">
<xs:annotation>
<xs:appinfo>
<jaxb:typesafeEnumMember name="SIX"/>
</xs:appinfo>
</xs:annotation>
</xs:enumeration>
</xs:restriction>
</xs:simpleType>
I end up with the following enum in Java (import statements and comments removed)
#XmlEnum
public enum Test {
#XmlEnumValue("4")
FOUR("4"),
#XmlEnumValue("6")
SIX("6");
private final String value;
Test(String v) {
value = v;
}
public String value() {
return value;
}
public static Test fromValue(String v) {
for (Test c: Test.values()) {
if (c.value.equals(v)) {
return c;
}
}
throw new IllegalArgumentException(v.toString());
}
}
This is exactly what I want... except for the public String value() method. I would expect the method to be called public String getValue() according to Sun's naming conventions. That way I can easily use it in a JSP-page using EL. Now I have to work my way around it.
Does anybody have any experience in further tweaking the XJC compilation to a more useful enumeration with a getValue() method, instead of a value() method? Or can I add a method or something?
P.S. This occurred in v2.0.3 of JAXB. I downloaded the latest version v2.1.8 and it's the same there...
There's nothing in the JAXB spec that seems to allow this change. I think the only way to do this would be to write a JAXB Plugin.
you could create a small variant of the generated class that only differs from the generated one for the name of this method. then at runtime, you have to make sure your variant is loaded instead of the generated one, playing the classloader game.
Of course, this can only work is the original XSD doesn't change often.

Resources