Can not deserialize instance of java.util.ArrayList out of VALUE_STRING

JavaJsonArraylistJacksonJson Deserialization

Java Problem Overview


I have a REST service built with Jersey and deployed in the AppEngine. The REST service implements the verb PUT that consumes an application/json media type. The data binding is performed by Jackson.

The verb consumes an enterprise-departments relation represented in JSON as

{"name":"myEnterprise", "departments":["HR","IT","SC"]}

On the client side, I use gson to convert the JSON representation into a java object. Then, I pass the object to my REST service and it works fine.

Problem:

When my JSON representation has only one item in the collection

{"name":"myEnterprise", "departments":["HR"]}

the service cannot deserialize the object.

ATTENTION: /enterprise/enterprise: org.codehaus.jackson.map.JsonMappingException: 
Can not deserialize instance of java.util.ArrayList out of VALUE_STRING token at 
[Source: org.mortbay.jetty.HttpParser$Input@5a9c5842; line: 1, column: 2

As reported by other users, the solution is to add the flag ACCEPT_SINGLE_VALUE_AS_ARRAY (e.g., https://stackoverflow.com/questions/14319573/jersey-can-not-deserialize-instance-of-arraylist-out-of-string). Nevertheless, I am not controlling an ObjectMapper because in the service side it is transparently made by Jackson.

Question:

Is there a way to configure the ObjectMapper on the service side to enable ACCEPT_SINGLE_VALUE_AS_ARRAY? annotations? web.xml?

Code details

Java object:

@XmlRootElement
public class Enterprise {
	private String name;
	private List<String> departments;
	
	public Enterprise() {}

	public String getName() {
		return name;
	}
	public void setName(String name) {
    	this.name = name;
   	}
    public List<String> getDepartments() {
		return departments;
	}
	public void setDepartments(List<String> departments) {
		this.departments = departments;
	}
}

The REST service side:

	@PUT
	@Consumes(MediaType.APPLICATION_JSON)
	@Produces(MediaType.APPLICATION_JSON)
	@Path("/enterprise") 
   	public Response putEnterprise(Enterprise enterprise,
			@Context HttpServletRequest req){
         ...
	}

Client side:

...
String jsonString = "{\"name\":\"myEnterprise\", \"departments\":[\"HR\"]}";
Enterprise enterprise = gson.fromJson(jsonString, Enterprise.class);
System.out.println(gson.toJson(enterprise));
response = webResource              
           .type(MediaType.APPLICATION_JSON)
           .put(ClientResponse.class,enterprise);
if (response.getStatus() >= 400) {
        throw new RuntimeException("Failed : HTTP error code : " + response.getStatus());
}
...

Java Solutions


Solution 1 - Java

This is the solution for my old question:

I implemented my own ContextResolver in order to enable the DeserializationConfig.Feature.ACCEPT_SINGLE_VALUE_AS_ARRAY feature.

package org.lig.hadas.services.mapper;

import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.ext.ContextResolver;
import javax.ws.rs.ext.Provider;

import org.codehaus.jackson.map.DeserializationConfig;
import org.codehaus.jackson.map.ObjectMapper;

@Produces(MediaType.APPLICATION_JSON)
@Provider
public class ObjectMapperProvider implements ContextResolver<ObjectMapper>
{
   ObjectMapper mapper;

   public ObjectMapperProvider(){
	   mapper = new ObjectMapper();
	   mapper.configure(DeserializationConfig.Feature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true);
   }
   @Override
   public ObjectMapper getContext(Class<?> type) {
       return mapper;
   }
}

And in the web.xml I registered my package into the servlet definition...

<servlet>
	<servlet-name>...</servlet-name>
	<servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class>
	<init-param>
		<param-name>com.sun.jersey.config.property.packages</param-name>
		<param-value>...;org.lig.hadas.services.mapper</param-value>		
	</init-param>
    ...
</servlet>

... all the rest is transparently done by jersey/jackson.

Solution 2 - Java

Setting this attribute to ObjectMapper instance works,

objectMapper.enable(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY);

Solution 3 - Java

do you try

[{"name":"myEnterprise", "departments":["HR"]}]

the square brace is the key point.

Solution 4 - Java

from Jackson 2.7.x+ there is a way to annotate the member variable itself:

 @JsonFormat(with = JsonFormat.Feature.ACCEPT_SINGLE_VALUE_AS_ARRAY)
 private List<String> newsletters;

More info here: Jackson @JsonFormat

Solution 5 - Java

For people that find this question by searching for the error message, you can also see this error if you make a mistake in your @JsonProperty annotations such that you annotate a List-typed property with the name of a single-valued field:

@JsonProperty("someSingleValuedField") // Oops, should have been "someMultiValuedField"
public List<String> getMyField() { // deserialization fails - single value into List
  return myField;
}

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionManoloView Question on Stackoverflow
Solution 1 - JavaManoloView Answer on Stackoverflow
Solution 2 - JavaKannan RamamoorthyView Answer on Stackoverflow
Solution 3 - JavaRichard ChenView Answer on Stackoverflow
Solution 4 - JavatheINtoyView Answer on Stackoverflow
Solution 5 - JavaAndrew SpencerView Answer on Stackoverflow