Can not deserialize instance of java.util.ArrayList out of START_OBJECT token

匿名 (未验证) 提交于 2019-12-03 02:44:02

问题:

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

回答1:

The problem is the JSON - this cannot, by default, be deserialized into a Collection because it's not actually a JSON Array - that would look like this:

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

Since you're not controlling the exact process of deserialization (RestEasy does) - a first option would be to simply inject the JSON as a String and then take control of the deserialization process:

Collection readValues = new ObjectMapper().readValue(jsonAsString, new TypeReference>() { }); 

You would loose a bit of the convenience of not having to do that yourself, but you would easily sort out the problem.

Another option - if you cannot change the JSON - would be to construct a wrapper to fit the structure of your JSON input - and use that instead of Collection.

Hope this helps.



回答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 {     ... } 


标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!