Null handling when Jersey unmarshals JSON via JAXB? - jaxb

Jersey appears to have a set of predetermined default values for JSON nulls while unmarshalling via JAXB: String => "", Integer => 0, ... is there anyway to easily control these values either with configuration or annotations?
Using Jersey's POJO mapping I get what I want, which is to map JSON null to Java null, but for other reasons we really need to use JAXB.
It seems like this should be simple however I'm drawing a blank.
Thanks.

Note: I'm the EclipseLink JAXB (MOXy) lead and a member of the JAXB (JSR-222) expert group.
The JAXB (JSR-222) specification does not cover JSON-binding so what you are experiencing is Jersey code interacting with the JAXB reference implementation producing some odd results wrt null handling. EclipseLink MOXy is a JAXB compliant implementation that also offers JSON binding that has more natural null handling.
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class Customer {
private String firstName;
#XmlElement(nillable=true)
private String lastName;
}
If this was marshalled to JSON the result would be the following. Null values are not marshalled to JSON unless they are annotated with #XmlElement(nillable=true):
{
lastName: null
}
For More Information
http://blog.bdoughan.com/2012/04/binding-to-json-xml-handling-null.html
Jersey Integration
http://blog.bdoughan.com/2012/05/moxy-as-your-jax-rs-json-provider.html
https://github.com/jersey/jersey/tree/master/examples/json-moxy

Related

Handling collections of domain objects in JAXB and Spring

We have a Spring Integration application which uses a JDBC poller together with a RowMapper to read from a database and output a collection of domain objects (presuming the result set returned more than one row).
The domain objects are then marshalled into XML. When using Castor as the marshaller, this works ok, and the XML represents a collection of the domain objects:
<array-list>
<order>
<orderID>23940210</orderID>
...
</order>
<order>
...
</array-list>
We now wish to switch from Castor to JAXB.
This is the definition of the JAXB marshaller in XML:
<oxm:jaxb2-marshaller id="jaxbMarshallerBean">
<oxm:class-to-be-bound name="com.mycompany.Order" />
</oxm:jaxb2-marshaller>
... the JAXB marshaller is used as the transformer used in the Spring Integration chain ..
<int:chain input-channel="input" output-channel="output-jms">
<si-xml:marshalling-transformer id="defaultMarshaller" marshaller="jaxbMarshallerBean" />
</int:chain>
and of course the domain class is annotated:
#XmlRootElement(namespace ="Order")
public class Order{
...
#XmlElement(name="OrderID")
public String getOrderId() {
return orderId;
}
Now, the following exception is thrown:
org.springframework.oxm.UncategorizedMappingException:
Unknown JAXB exception;
nested exception is javax.xml.bind.JAXBException:
class java.util.ArrayList nor any of its super class is known to this context.
It looks like JAXB does not like the fact that it is handling a collection of domain objects. What is the correct way to configure or handle this?
Thanks very much
Surely you want an unmarshalling transformer to go from XML to POJO.
I don't know if there is a way to configure JAXB to handle it directly, but you could add an XPath splitter before the unmarshaller and an aggregator after it.

JAXB unmarshalling Custom entities without annotation

We have an xml file which we need to unmarshall(convert into a Java Object). Now the Java object is of third party and I cannot annotate it for unmarshalling. Any idea as to how I can Unmarshal without annotation. Please find my code snippet below
JAXBContext context;
try {
context = JAXBContext.newInstance(Abc.class);
Unmarshaller unMarshaller = context.createUnmarshaller();
Abc abc= (Abc) unMarshaller.unmarshal(new FileInputStream("C:\\Documents and Settings\\sandeep.nair\\Desktop\\abc.xml"));
} catch (JAXBException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}catch (Exception e){
}
I am getting the following exception. One for No argument Constructor(I know I can solve this by adding annotation for Adapter but I want to know or see a sample snippet for handling it without annotation)
Similarly I am getting another message as to Interfaces cannot be handle by JAXB.
com.sun.xml.bind.v2.runtime.IllegalAnnotationsException: 5 counts of IllegalAnnotationExceptions
java.sql.Timestamp does not have a no-arg default constructor.
this problem is related to the following location:
at java.sql.Timestamp
at public java.sql.Timestamp com.test.Abc.getSomeTimestamp()
at com.riteaid.entities.customer.Customer
com.test.Def does not have a no-arg default constructor.
this problem is related to the following location:
at com.test.Def...
java.sql.Date does not have a no-arg default constructor.
this problem is related to the following location:
at java.sql.Date
...
com.test.Ghi is an interface, and JAXB can't handle interfaces.
this problem is related to the following location:
...
com.test.Ghi does not have a no-arg default constructor.
this problem is related to the following location:
..
Note: I'm the EclipseLink JAXB (MOXy) lead and a member of the JAXB (JSR-222) expert group.
JAXB is configuration by exception , so you only need to add annotations where you want to override the default mapping behaviour:
http://blog.bdoughan.com/2012/07/jaxb-no-annotations-required.html
The standard way of supplying metadata is through annotations. In situations where you can't annotate such as 3rd party classes, then you may be interested in MOXy's external mapping document extension.
http://blog.bdoughan.com/2010/12/extending-jaxb-representing-annotations.html
http://blog.bdoughan.com/2012/04/extending-jaxb-representing-metadata-as.html
You can use an XmlAdapter to handle the javax.sql.Date and javax.sql.Timestamp types:
jaxb unmarshal timestamp

Why JAXB is not marshalling my own subclass

I have a jaxb object which can be marshalled successfully, and it has a list object, then I make a new object like below
public class Sub extends SuperJAXBClass{
#Override
public List getList1(){
//override here
return ...;
}
}
Then the code like below:
SuperJAXBClass sjc=new Sub();
marshall(sjc)
Then I found the List1 in Sub is not marshalled successfully.
Any one knows why this happens?
How to solve it?
You could do one of the following:
Option #1 - #XmlSeeAlso Annotation
JAXB (JSR-222) implementations can not use Java reflection to determine all the possible suclasses. As a work around you can annotate the super class with the #XmlSeeAlso annotation that provides JAXB a reference to the subclasses.
#XmlSeeAlso({Sub.class})
public class SuperJAXBClass {
}
Option #2 - Pass the Subclass When Creating JAXBContext
If you include the subclass when creating the JAXBContext then JAXB implementations will be aware of it. When a subclass is passed in metadata for the super classes is also created.
JAXBContext.newInstance(Sub.class);

JAXB is not picking up #XmlJavaTypeAdapter

I have the following class that I need to serialize as XML:
#XmlAccessorType(XmlAccessType.FIELD)
public class Position {
#XmlElement(name = "Quantity", required = true)
private DecimalQuantity quantity;
...
}
I have put an XmlJavaTypeAdapter on the DecimalQuantity class because I want it to be serialized simply as a BigDecimal without the DecimalQuantity wrapper.
#XmlJavaTypeAdapter(DecimalQuantityAdapter.class)
#Embeddable
public class DecimalQuantity {
private BigDecimal value;
...
}
Here's the very simple DecimalQuantityAdapter class:
public class DecimalQuantityAdapter
extends XmlAdapter<BigDecimal, DecimalQuantity> {
public DecimalQuantity unmarshal(BigDecimal val) throws Exception {
return new DecimalQuantity(val);
}
public BigDecimal marshal(DecimalQuantity val) throws Exception {
return val.getValue();
}
}
I have a unit test that shows that the adapter is working correctly. The following Order object that has a DecimalQuantity gets serialized correctly (notice that this test class looks almost identical to the Position class above):
#XmlAccessorType(XmlAccessType.FIELD)
#XmlRootElement(name = "Order")
public class Order {
#XmlElement(name = "Quantity", required = true)
private DecimalQuantity quantity;
...
}
This gets serialized as shown below - no wrapper around the decimal number - life is good!
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Order>
<Quantity>10.2</Quantity>
</Order>
The trouble starts when I try to use DecimalQuantity in other maven projects. For example, the Position class shown at the beginning of this post is in a different maven project. The web service that uses the Position class is in yet another maven project. When the web service tries to deserialize DecimalQuantity, it does not know what DecimalQuantity is and is not able to pick up the DecimalQuantityAdapter. This is the error I get:
Caused by: javax.xml.bind.JAXBException:
class org.archfirst.common.quantity.DecimalQuantity nor any of its super class is known to this context.
at com.sun.xml.bind.v2.runtime.JAXBContextImpl.getBeanInfo(JAXBContextImpl.java:594)
at com.sun.xml.bind.v2.runtime.XMLSerializer.childAsXsiType(XMLSerializer.java:648)
... 53 more
I have event tried to add the #XmlJavaTypeAdapter annotation on the attribute itself, but JAXB does not pick it up. The only way to get rid of the exception is to put an #XmlSeeAlso({DecimalQuantity.class}) on the Position class itself. However, this disables the adapter and I get the following (undesired) serialization:
<Quantity xsi:type="ns2:decimalQuantity" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"/>
Any idea where the problem is? I feel it has something to do with the visibility of JAXB annotations on DecimalQuantity and DecimalQuantityAdapter across packages/projects.
Thanks.
Naresh
Ok, I finally found the problem. My unit test was picking up the JAXB implementation in the Java runtime, whereas my real application (a web service) was picking up the JAXB implementation from GlassFish. Apparently the implementation bundled with GlassFish (2.2.1.1) cannot handle my use case. I proved it by forcing my unit test to use jaxb-impl-2.2.1.1.jar. Also it seems that the bug has been fixed in the latest JAXB implementation (2.2.3-1), but I am struggling to figure out how to replace GlassFish's implementation with this new version (see my post here).
Are you sure the problem is with the XmlJavaTypeAdapter for decimals, not the DecimalQuantity type. Because the exception you've posted is the one that happens when JAXB encounters a value of unknown class.
What happens if you omit the #XmlJavaTypeAdapter annotation? I know it probably can't work the way you intend, but what is the error message? Isn't it the same?
As you wrote the exception is gone when you added:
#XmlSeeAlso({DecimalQuantity.class})
I would leave the annotation in the code and try to find the reason why the adapter doesn't work.
Can you debug in the your XML adapter and/or add some trace output there, just to make sure the adapter really returns a non-empty String?

JAXB cyclic reference avoidance using #XmlIDREF

I'm using JAXB in a web service with some slightly complex objects. One of the objects, Sensor, has a list of other objects it can communicate with, which necessarily can include itself (behavior that cannot be changed), leading to a cyclic reference during marshalling to XML.
#XmlAccessorType(XmlAccessType.FIELD)
public class Sensor extends BaseObject {
private ArrayList<SensorCommLink> sensorCommLinks;
}
#XmlAccessorType(XmlAccessType.FIELD)
public class SensorCommLink {
#XmlIDREF
private BaseObject receiver;
#XmlIDREF
private Sensor cueingSensor;
}
#XmlAccessorType(XmlAccessType.FIELD)
public abstract class BaseObject {
#XmlElement
#XmlID
private String id;
}
As shown above I solved this using #XmlIDREF and #XmlID and it works very nicely.
The client-side code generated via wsimport marshals the objects to XML and the server is able to unmarshal them perfectly.
The problem I'm experiencing is that for some reason on the server side I am getting a cyclic reference exception when I try to marshal a Sensor object. The maddening part is that the server-side code contains the JAXB annotations that are used by wsimport to create the client-side code, which works great, yet I can't marshal server-side Sensors due to the cycle.
I tried copying all of the extra annotations JAXB adds to the client-side code onto the server-side classes thinking perhaps there was a runtime bug in JAXB that was preventing it from properly applying the #XmlIDREF annotation. No luck there.
Perhaps there's something very basic I'm missing here but this issue is driving me a little batty and I'm at a dead stop while I try to figure it out.
One thing I did notice that I'm investigating is that some of the namespaces on the generated client-side objects aren't what I expected, though the code works. I'm curious to see if somehow a namespace issue on the server is causing the IDREF marshalling to bomb.
Any chance on the server side it is processing properties (get/set) instead of fields (instance variables). You can enforce field access in the following way:
#XmlAccessorType(XmlAccessType.FIELD)
public class SensorCommLink {
#XmlIDREF
private BaseObject receiver;
#XmlIDREF
private Sensor cueingSensor;
}
Or you could annotate the get methods.

Resources