Background
Jersey is a JAX-RS implementation which enables us writing REST resources by annotating interfaces/implementation in server side. Both XML and JSON data interchange format is supported by jersey.
It uses JAXB for xml data interchange and JAXB again (with a JSON parser) for JSON interchange. JSON that's produced follows a specific notation - its JAXB based and can come with it's own issues:
a. array structure not formed in resultant JSON for single element arrays
e.g. {names : "Harry"} rather than {names : ["Harry"]}
b. exact enum name not used - name is taken from JAXB XML annotation
@XmlEnumValue("Success")
SUCCESS("Success"),
@XmlEnumValue("Failure")
FAILURE("Failure")
produces {ack : "Success"} rather than {ack : "SUCCESS"}
These impose problems upon de-serializing JSON. Easy solution is to ask Jersey to use Jackson, an open-source, famous, so-far-best JSON parser (my opinion) for serialization which solves all stated issues above. But its not that easy..
Server side
Have Jackson jar (alone) in classpath
Make sure you
have Jackson jar in classpath along with Jersey. Also for safety
reasons, make sure any other JSON parser is not present, for e.g.
Jettison. With multiple JSON parsers present in classpath, we can't
guarantee that Jersey will pick Jackson. And with Jettison, not both of
above issues can be resolved. For sample, at the time of writing,
Jettison returns arrays with single element not enclosed in [] (issue 1).
Enable JSON POJO mapping
There are several JSON annotations that are available. POJO notation is one such which is vanilla POJO -> JSON -> POJO way and to use this we need to enable JSON POJO mapping (as mentioned in link above):
<init-param>
<param-name>com.sun.jersey.api.json.POJOMappingFeature</param-name>
<param-value>true</param-value>
</init-param>
Even with this, Jersey still might use JAXB for JSON serialization! Read further..
Jackson JAX-RS provider
We might need to inject a custom provider to make Jersey use Jackson. Fortunately, we don't need to write one as one is already present in jackson-jaxrs jar (hence add this as dependency). Now to ask Jersey to use it, add the package "org.codehaus.jackson.jaxrs" to scan for it:
<init-param>
<param-name>com.sun.jersey.config.property.packages</param-name>
<param-value>org.codehaus.jackson.jaxrs;<your other packages with resources> </param-value>
</init-param>
<param-name>com.sun.jersey.config.property.packages</param-name>
<param-value>org.codehaus.jackson.jaxrs;<your other packages with resources> </param-value>
</init-param>
Reference : SO post
Client side
Per POJO notation, create client this way
ClientConfig clientConfig = new DefaultClientConfig();
clientConfig.getFeatures().put(JSONConfiguration.FEATURE_POJO_MAPPING, Boolean.TRUE);
// force client to use Jackson JAX-RS provider (one in org.codehaus.jackson.jaxrs)
// without it i faced issues with Jersey unable to locate my custom resolver with custom serialization/deserialization logic, in my client side
// see this http://crazytechbuddy.blogspot.com/2012/06/jackson-custom-serializationdeserializa.html
clientConfig.getClasses().add(JacksonJsonProvider.class);
Client client = Client.create(clientConfig);
and specify the "Content-type" as "application/json" in header
<Your response class> response = resource.header("Content-Type", MediaType.APPLICATION_JSON).
accept(MediaType.APPLICATION_JSON).get(<Your response class>.class);
That's it, now Jersey will use Jackson in both serialization and de-serialization, in both server side and client side.
Thanks, this was just what I needed.
ReplyDeleteWhen using server side, is there a way to tell jersey to use jackson just on specific methods ?