lpinto.eu
lpinto.eu

Reputation: 2127

Jersey: Can not deserialize instance of ArrayList out of String

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!

Upvotes: 13

Views: 37823

Answers (5)

steven35
steven35

Reputation: 4017

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;
}

Upvotes: 5

Zaric
Zaric

Reputation: 11

{"results": {"place": {"lang": "en-US","woeid": "2168606"}}}

&

{"results": {"place": [{"lang": "en-US","woeid": "2168606"}, {"lang": "en-US","woeid": "2168606"}]}}

this type how to do ...when using yahoo place api

Upvotes: 1

Manju
Manju

Reputation: 64

It worked for me by adding @JsonSerialize to the class

@JsonSerialize
Public class Person {
    List<String> names;
}

Upvotes: 1

lpinto.eu
lpinto.eu

Reputation: 2127

After a long day, and another... after reading lots of wikis and faqs, it finally works.

What I did:

  1. Use Jackson
  2. Force Jackson providers usage
  3. Define custom deserializer
  4. activate the ACCEPT_SINGLE_VALUE_AS_ARRAY flag
  5. Fix dependencies

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

Upvotes: 14

rmhartog
rmhartog

Reputation: 2323

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().

Upvotes: 1

Related Questions