Create an account

Very important

  • To access the important data of the forums, you must be active in each forum and especially in the leaks and database leaks section, send data and after sending the data and activity, data and important content will be opened and visible for you.
  • You will only see chat messages from people who are at or below your level.
  • More than 500,000 database leaks and millions of account leaks are waiting for you, so access and view with more activity.
  • Many important data are inactive and inaccessible for you, so open them with activity. (This will be done automatically)


Thread Rating:
  • 667 Vote(s) - 3.55 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Can not deserialize instance of java.util.ArrayList out of START_OBJECT token

#1
I'm trying to POST a `List` of custom objects.
My JSON in request body is this:

{
"collection": [
{
"name": "Test order1",
"detail": "ahk ks"
},
{
"name": "Test order2",
"detail": "Fisteku"
}
]
}

Server side code that handles the request:

import java.util.Collection;

import javax.ws.rs.Consumes;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;


@Path(value = "/rest/corder")
public class COrderRestService {

@POST
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public Response postOrder(Collection<COrder> orders) {
StringBuilder stringBuilder = new StringBuilder();
for (COrder c : orders) {
stringBuilder.append(c.toString());
}
System.out.println(stringBuilder);
return Response.ok(stringBuilder, MediaType.APPLICATION_JSON).build();
}
}

Entity `COrder`:

import javax.xml.bind.annotation.XmlRootElement;

@XmlRootElement
public class COrder {
String name;
String detail;

@Override
public String toString() {
return "COrder [name=" + name + ", detail=" + detail
+ ", getClass()=" + getClass() + ", hashCode()=" + hashCode()
+ ", toString()=" + super.toString() + "]";
}
}

But an exception is thrown:

SEVERE: Failed executing POST /rest/corder
org.jboss.resteasy.spi.ReaderException: org.codehaus.jackson.map.JsonMappingException: Can not deserialize instance of java.util.ArrayList out of START_OBJECT token
at [Source: org.apache.catalina.connector.CoyoteInputStream@6de8c535; line: 1, column: 1]
at org.jboss.resteasy.core.MessageBodyParameterInjector.inject(MessageBodyParameterInjector.java:183)
at org.jboss.resteasy.core.MethodInjectorImpl.injectArguments(MethodInjectorImpl.java:88)
at org.jboss.resteasy.core.MethodInjectorImpl.invoke(MethodInjectorImpl.java:111)
at org.jboss.resteasy.core.ResourceMethodInvoker.invokeOnTarget(ResourceMethodInvoker.java:280)
at org.jboss.resteasy.core.ResourceMethodInvoker.invoke(ResourceMethodInvoker.java:234)
at org.jboss.resteasy.core.ResourceMethodInvoker.invoke(ResourceMethodInvoker.java:221)
at org.jboss.resteasy.core.SynchronousDispatcher.invoke(SynchronousDispatcher.java:356)
at org.jboss.resteasy.core.SynchronousDispatcher.invoke(SynchronousDispatcher.java:179)
at org.jboss.resteasy.plugins.server.servlet.ServletContainerDispatcher.service(ServletContainerDispatcher.java:220)
at org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher.service(HttpServletDispatcher.java:56)
at org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher.service(HttpServletDispatcher.java:51)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:728)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:305)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:51)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:243)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:222)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:123)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:502)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:171)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:100)
at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:953)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:118)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:408)
at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1041)
at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:603)
at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:312)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
at java.lang.Thread.run(Thread.java:724)
Reply

#2
**This will work:**

The problem may happen when you're trying to read a list with a single element as a **JsonArray** rather than a **JsonNode** or vice versa.

Since you can't know for sure if the returned list contains a single element *(so the json looks like this **{...}**)* or multiple elements *(and the json looks like this **[{...},{...}]**)* -
you'll have to check in runtime the type of the element.

It should look like this:

***(Note: in this code sample I'm using com.fasterxml.jackson)***

String jsonStr = response.readEntity(String.class);
ObjectMapper mapper = new ObjectMapper();
JsonNode rootNode = mapper.readTree(jsonStr);

// Start by checking if this is a list -> the order is important here:
if (rootNode instanceof ArrayNode) {
// Read the json as a list:
myObjClass[] objects = mapper.readValue(rootNode.toString(), myObjClass[].class);
...
} else if (rootNode instanceof JsonNode) {
// Read the json as a single object:
myObjClass object = mapper.readValue(rootNode.toString(), myObjClass.class);
...
} else {
...
}
Reply

#3
Instead of the JSON document, you can update the ObjectMapper object like below :

ObjectMapper mapper = new ObjectMapper();
mapper.configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true);
Reply

#4
I had this issue on a REST API that was created using Spring framework. Adding a @ResponseBody annotation (to make the response JSON) resolved it.
Reply

#5
Normally we face this issue when there is a problem mapping JSON node with that of Java object. I faced the same issue because in the swagger the node was defined as of Type array and the JSON object was having only one element , hence the system was having difficulty in mapping one element list to an array.

In Swagger the element was defined as

Test:
"type": "array",
"minItems": 1,
"items": {
"$ref": "#/definitions/TestNew"
}

While it should be

Test:
"$ref": "#/definitions/TestNew"

And `TestNew` should be of type array


Reply

#6
Related to Eugen's answer, you can solve this particular case by creating a wrapper POJO object that contains a `Collection<COrder>` as its member variable. This will properly guide Jackson to place the actual `Collection` data inside the POJO's member variable and produce the JSON you are looking for in the API request.

Example:

public class ApiRequest {

@JsonProperty("collection")
private Collection<COrder> collection;

// getters
}

Then set the parameter type of `COrderRestService.postOrder()` to be your new `ApiRequest` wrapper POJO instead of `Collection<COrder>`.
Reply

#7
Same issue:

```
com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot deserialize instance of `java.util.UUID` out of START_OBJECT token
```

What caused it was the following:

ResponseEntity<UUID> response = restTemplate.postForEntity("/example/", null, UUID.class);


In my test I purposely set the request as null (no content POST). As previously mentioned, the cause for the OP was the same because the request didn't contain a valid JSON, so it couldn't be automatically identified as an application/json request which was the limitation on the server (`consumes = "application/json"`). A valid JSON request would be. What fixed it was populating an entity with null body and json headers explicitly.


HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
HttpEntity request = new HttpEntity<>(null, headers);
ResponseEntity<UUID> response = restTemplate.postForEntity("/example/", request, UUID.class);

Reply

#8
In my case, the error was being shown because when I was reading my JSON file using Jackson library, my JSON file contained only 1 object. Hence it started with "{" and ended with "}". But while reading it and storing it in a variable, I was storing it in an Array object (As in my case, there could be more than 1 object).

Hence, I added "[" in the start and "]" in the end of my JSON file to convert it into an array of object and it worked perfectly fine without any error.
Reply

#9
I ran into this same problem these days and maybe some more detail might be helpful to somebody else.

I was looking some security guidelines for REST APIs and crossed a [very intriguing issue][1] with json arrays. Check the link for details, but basically, you should wrap them within an Object as we already saw in this post question.

So, instead of:

[
{
"name": "order1"
},
{
"name": "order2"
}
]

It's advisable that we always do:

{
"data": [
{
"name": "order1"
},
{
"name": "order2"
}
]
}

This is pretty straight-forward when you doing a *GET*, but might give you some trouble if, instead, your trying to *POST/PUT* the very same json.

In my case, I had more than one *GET* that was a *List<some DTO object>* and more than one *POST/PUT* that would receive the very same json.

So what I end up doing was to use a very simple **Wrapper<T>** object for a **List<T>**:

public class Wrapper<T> {
private List<T> data;

public Wrapper() {}

public Wrapper(List<T> data) {
this.data = data;
}
public List<T> getData() {
return data;
}
public void setData(List<T> data) {
this.data = data;
}
}

Serialization of my lists were made with a *@ControllerAdvice*:

@ControllerAdvice
public class JSONResponseWrapper implements ResponseBodyAdvice<Object> {

@Override
public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
return true;
}

@Override
@SuppressWarnings("unchecked")
public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
if (body instanceof List) {
return new Wrapper<>((List<Object>) body);
}
else if (body instanceof Map) {
return Collections.singletonMap("data", body);
}
return body;
}
}

So, all the Lists and Maps where wrapped over a *data* object as below:

{
"data": [
{...}
]
}

Deserialization was still default, just using de **Wrapper<T>** Object:

@PostMapping("/resource")
public ResponseEntity<Void> setResources(@RequestBody Wrapper<ResourceDTO> wrappedResources) {
List<ResourceDTO> resources = wrappedResources.getData();
// your code here
return ResponseEntity
.ok()
.build();
}

That was it! Hope it helps someone.

**Note:** tested with SpringBoot *1.5.5.RELEASE*.

[1]:

[To see links please register here]



Reply

#10
As mentioned above the following would solve the problem: `mapper.configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true);`

However in my case the provider did this [0..1] or [0..*] serialization rather as a bug and I could not enforce fixing. On the other hand it did not want to impact my strict mapper for all other cases which needs to be validated strictly.

So I did a Jackson NASTY HACK (which should not be copied in general ;-) ), especially because my SingleOrListElement had only few properties to patch:

@JsonProperty(value = "SingleOrListElement", access = JsonProperty.Access.WRITE_ONLY)
private Object singleOrListElement;

public List<SingleOrListElement> patch(Object singleOrListElement) {
if (singleOrListElement instanceof List) {
return (ArrayList<SingleOrListElement>) singleOrListElement;
} else {
LinkedHashMap map = (LinkedHashMap) singleOrListElement;
return Collections.singletonList(SingletonList.builder()
.property1((String) map.get("p1"))
.property2((Integer) map.get("p2"))
.build());
}



Reply



Forum Jump:


Users browsing this thread:
1 Guest(s)

©0Day  2016 - 2023 | All Rights Reserved.  Made with    for the community. Connected through