无法从START_OBJECT标记中反序列化java.util.ArrayList的实例

isa*_*sah 102 java spring jax-rs resteasy jackson

我正在尝试POST一个List自定义对象.请求正文中的我的JSON是这样的:

{
    "collection": [
        {
            "name": "Test order1",
            "detail": "ahk ks"
        },
        {
            "name": "Test order2",
            "detail": "Fisteku"
        }
    ]
}
Run Code Online (Sandbox Code Playgroud)

处理请求的服务器端代码:

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();
    }
}
Run Code Online (Sandbox Code Playgroud)

实体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() + "]";
    }
}
Run Code Online (Sandbox Code Playgroud)

但抛出异常:

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)
Run Code Online (Sandbox Code Playgroud)

Eug*_*gen 135

问题是JSON - 默认情况下,这不能反序列化为a,Collection因为它实际上不是JSON数组 - 看起来像这样:

[
    {
        "name": "Test order1",
        "detail": "ahk ks"
    },
    {
        "name": "Test order2",
        "detail": "Fisteku"
    }
]
Run Code Online (Sandbox Code Playgroud)

既然你没有控制反序列化的确切过程(RestEasy会这样做) - 第一个选择是简单地将JSON注入String,然后控制反序列化过程:

Collection<COrder> readValues = new ObjectMapper().readValue(
    jsonAsString, new TypeReference<Collection<COrder>>() { }
);
Run Code Online (Sandbox Code Playgroud)

你可以省去一些不必自己做的便利,但你很容易理清问题.

另一个选择 - 如果你不能改变JSON - 将构造一个包装器以适应你的JSON输入的结构 - 并使用它而不是Collection<COrder>.

希望这可以帮助.

  • 太棒了,我把它包装在集合中,因为 Resteasy 文档中有一个示例,但它是使用 XML 的。 (2认同)
  • @isah很好,请你在这里分享那个resteasy链接 (2认同)

Sal*_*twa 35

您可以像下面那样更新ObjectMapper对象,而不是JSON文档:

ObjectMapper mapper = new ObjectMapper();
mapper.configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true);
Run Code Online (Sandbox Code Playgroud)


For*_*est 16

在与这个问题斗争了太久之后,这里有一个超级简单的解决方案。

我的控制器正在寻找

@RequestBody List<String> ids
Run Code Online (Sandbox Code Playgroud)

我的请求正文为

{
    "ids": [
        "1234",
        "5678"
     ]
}
Run Code Online (Sandbox Code Playgroud)

解决方案就是简单地改变身体

["1234", "5678"]
Run Code Online (Sandbox Code Playgroud)

是的。就这么简单。


Adi*_*l B 10

与 Eugen 的回答相关,您可以通过创建一个包含 aCollection<COrder>作为其成员变量的包装器 POJO 对象来解决这个特殊情况。这将正确引导 Jackson 将实际Collection数据放入 POJO 的成员变量中,并生成您在 API 请求中查找的 JSON。

例子:

public class ApiRequest {

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

   // getters
}
Run Code Online (Sandbox Code Playgroud)

然后将 的参数类型设置为COrderRestService.postOrder()新的 ApiRequest包装器 POJO 而不是Collection<COrder>.


Nao*_*Bar 7

这将有效:

当您尝试将单个元素作为JsonArray而不是JsonNode读取列表时,可能会发生此问题,反之亦然.

因为你无法确定返回的列表是否包含单个元素(因此json看起来像这个{...})或多个元素(并且json看起来像这个[{...},{... }]) - 你必须在运行时检查元素的类型.

它应该如下所示:

(注意:在此代码示例中我使用的是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 {
    ...
}
Run Code Online (Sandbox Code Playgroud)


Suh*_*eer 7

@JsonFormat(with = JsonFormat.Feature.ACCEPT_SINGLE_VALUE_AS_ARRAY)
private List< COrder > orders;
Run Code Online (Sandbox Code Playgroud)

  • 请提供完整的答案,其中包含代码块中的一些代码和一些评估答案正确性的文本 (5认同)

phi*_*zel 5

如上所述,以下内容可以解决该问题:mapper.configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true);

然而,在我的例子中,提供者将这个 [0..1] 或 [0..*] 序列化而不是作为一个错误,我无法强制修复。另一方面,它不想影响我对所有其他需要严格验证的情况的严格映射器。

所以我做了一次 Jackson NASTY HACK(一般情况下不应该复制;-)),特别是因为我的 SingleOrListElement 只有很少的属性需要修补:

@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());
  }
Run Code Online (Sandbox Code Playgroud)