I have a Detail object that has a property of object of type Product. Product has a property called xxx which is an arraylist. I do a GET on the URL with postman and the result looks like:
"Product": {
"id": "2",
"xxx": [
"price": "50"
},
{
"price": "60"
}
]
}
This result is good. But, in my Spring project, when I do a get Using RestTemplate as:
restTemplate.getForEntity("someurl", Detail.class).getBody();
I get correct results when xxx list contains 2 or more element. However, when there is only element in this list, I get an error:
org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: Can not deserialize instance of java.util.ArrayList out of START_OBJECT token; nested exception is com.fasterxml.jackson.databind.JsonMappingException: Can not deserialize instance of java.util.ArrayList out of START_OBJECT token
How do I fix this issue that I am facing with my call to restTemplate.getForEntity as above?
I suspect that your encounter this error not when your list contains one item but rather when you have no list at all but a plain object, thus the parser complains about a misplaced START_OBJECT
token.
To remedy this and without being able to edit Swagger-generated domain classes, you could set the deserialization feature
ObjectMapper objectMapper = new ObjectMapper(); // maybe injected
objectMapper.configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true);
directly on the ObjectMapper
that you are using.
This object mapper can now be put into the RestTemplate
config:
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class JsonControllerIT {
@LocalServerPort
private int port;
@Test
public void jsonWithSerializationFeatureSet() {
// given
RestTemplate restTemplate = new RestTemplate();
MappingJackson2HttpMessageConverter messageConverter = new MappingJackson2HttpMessageConverter();
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true);
messageConverter.setObjectMapper(objectMapper);
restTemplate.getMessageConverters().removeIf(m -> m.getClass().getName().equals(MappingJackson2HttpMessageConverter.class.getName()));
restTemplate.getMessageConverters().add(messageConverter);
// when
Detail detail = restTemplate.getForEntity("http://localhost:" + port + "/json", Detail.class).getBody();
// then
assertThat(detail.getSingleItemList().get(0)).isEqualTo(3);
}
}
If you use the Spring-approach keep in mind that you also can inject all those beans, they are created here straight-forward just show them better together.
You can also check this running example: