I have a json based REST Web Service implemented using: Jetty, Jersey, Jersey-JSON using Jackson.
One of my methods receives a Person instance, which has a field of type List<String>. i.e.:
Public class Person {
List<String> names;
}
If I call it with an array of names, all works ok! e.g.:
{ "names" : [ "Jhon", "Doe" ] }
But if the person has only one name, my client creates a single value element, e.g.:
{ "names" : "Jhon" }
When I try to call the service with a single value, I get an exception:
Can not deserialize instance of java.util.ArrayList out of VALUE_STRING token
Question:
How should I create/configure my web service, in order to be able to deserialize array field when they are sent to me as a single element.
--
I already read:
Jackson deserialization - with contained ArrayList<T>
and
How can I customize serialization of a list of JAXB objects to JSON?
and this that refer the last answer:
Jersey client can not deserializable json services - exception(can not deserialize instance)
Jaxb json missing brackets for one element array
But none of those fix the problem.
Thank you in advance!
After a long day, and another... after reading lots of wikis and faqs, it finally works.
What I did:
The story:
I was using Jersey 1.13 that uses (I belive) jaxb by default.
I changed it to use Jackson
<init-param>
<param-name>com.sun.jersey.api.json.POJOMappingFeature</param-name>
<param-value>true</param-value>
</init-param>
as described at:
https://stackoverflow.com/a/13895768/660990
This made my jersey use jackson, but the problem remains; jackson can't deserializer the array yet.
I forced the usage of Jackson providers:
<init-param>
<param-name>com.sun.jersey.config.property.packages</param-name>
<param-value>
your.project.packages;
org.codehaus.jackson.jaxrs</param-value>
</init-param>
https://stackoverflow.com/a/3143214/660990
Was a needed step, but not enough. It's necessary to activate the ACCEPT_SINGLE_VALUE_AS_ARRAY flag.
ObjectMapper objectMapper;
objectMapper = new ObjectMapper().configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true);
I needed define custom deserializer
public class MyResolver implements ContextResolver<ObjectMapper> {
@Override
public ObjectMapper getContext(final Class<?> objectType) {
...
return objectMapper;
}
}
in order to do that:
Customizing-ObjectMapper
After doing all of this it still didn't work...
After some more search time, I found:
Jackson 2.0 with Jersey 1.12
That discusses dependencies problems..
This reveled my problem, Jersey v1.13 ships with Jackson v1.9.2 I need Jackson v2.0
I removed dependency for jersey-json, because it included jackson 1.9.2:
<dependency>
<groupId>com.sun.jersey</groupId>
<artifactId>jersey-json</artifactId>
</dependency>
And directly declared dependency for:
<dependency>
<groupId>com.fasterxml.jackson.jaxrs</groupId>
<artifactId>jackson-jaxrs-json-provider</artifactId>
</dependency>
Reference:
jackson-jaxrs-json-provider
Note: this change removes the Jersey ability to use Jaxb or jettison.
Off topic, may be interesting for someone:
Configure Jersey/Jackson to NOT use @XmlElement field annotation for JSON field naming
You can do this with annotations at class, field or method level using the JsonFormat annotation
@JsonFormat(with = Feature.ACCEPT_SINGLE_VALUE_AS_ARRAY)
protected List<String> list;
public List<String> getList() {
return list;
}
public void setList(List<String> list) {
this.list = list;
}
Example 5.10 and 5.11 of this page explain your exact problem. You can configure the JSON builder to force arrays for single-item lists, like so:
JSONConfiguration.mapped().arrays("names").build()
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With