无法从START\ u对象标记中反序列化java.util.ArrayList的实例
我正试图发布自定义对象的无法从START\ u对象标记中反序列化java.util.ArrayList的实例,java,spring,jackson,jax-rs,resteasy,Java,Spring,Jackson,Jax Rs,Resteasy,我正试图发布自定义对象的列表。 请求正文中的JSON如下所示: { "collection": [ { "name": "Test order1", "detail": "ahk ks" }, { "name": "Test order2", "detail": "Fisteku" } ] } 处理请求的服务器端代码:
列表。
请求正文中的JSON如下所示:
{
"collection": [
{
"name": "Test order1",
"detail": "ahk ks"
},
{
"name": "Test order2",
"detail": "Fisteku"
}
]
}
处理请求的服务器端代码:
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();
}
}
但会引发一个异常:
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)
问题在于JSON—默认情况下,无法将其反序列化为集合
,因为它实际上不是JSON数组—如下所示:
[
{
"name": "Test order1",
"detail": "ahk ks"
},
{
"name": "Test order2",
"detail": "Fisteku"
}
]
由于您没有控制反序列化的确切过程(RestEasy会)-第一个选项是将JSON作为字符串注入,然后控制反序列化过程:
Collection<COrder> readValues = new ObjectMapper().readValue(
jsonAsString, new TypeReference<Collection<COrder>>() { }
);
Collection readValues=new ObjectMapper().readValue(
jsonAsString,新类型引用(){}
);
你会失去一点不必自己做这件事的便利,但你会很容易地解决问题
另一个选项——如果您无法更改JSON——将构建一个包装器以适合JSON输入的结构——并使用它而不是集合
希望这有帮助 这将起作用:
当您试图将单个元素作为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 {
...
}
您可以更新ObjectMapper对象,而不是JSON文档,如下所示:
ObjectMapper mapper = new ObjectMapper();
mapper.configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true);
我在使用Spring框架创建的RESTAPI上遇到了这个问题。添加@ResponseBody注释(使响应JSON)解决了这个问题。通常,当JSON节点与Java对象的节点映射出现问题时,我们会遇到这个问题。我面临同样的问题,因为在招摇过市中,节点被定义为数组类型,而JSON对象只有一个元素,因此系统很难将一个元素列表映射到数组
在Swagger中,元素被定义为
Test:
"type": "array",
"minItems": 1,
"items": {
"$ref": "#/definitions/TestNew"
}
虽然它应该是
Test:
"$ref": "#/definitions/TestNew"
而TestNew
应该是数组类型与Eugen的答案相关,您可以通过创建包含集合
作为其成员变量的包装器POJO对象来解决这种特殊情况。这将正确地指导Jackson将实际的集合
数据放在POJO的成员变量中,并生成您在API请求中寻找的JSON
例如:
public class ApiRequest {
@JsonProperty("collection")
private Collection<COrder> collection;
// getters
}
公共类请求{
@JsonProperty(“集合”)
私人收藏;
//吸气剂
}
然后将COrderRestService.postOrder()
的参数类型设置为新的APIREST
wrapper POJO,而不是CollectionDto response=softConvertValue(jsonData,Dto.class);
公共静态T softConvertValue(对象fromValue,类到ValueType)
{
ObjectMapper objMapper=新的ObjectMapper();
返回objMapper.configure(在未知属性上反序列化feature.FAIL,false)
.convertValue(fromValue、toValueType);
}
同一问题:
com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot deserialize instance of `java.util.UUID` out of START_OBJECT token
原因如下:
ResponseEntity<UUID> response = restTemplate.postForEntity("/example/", null, UUID.class);
@JsonFormat(with=JsonFormat.Feature.ACCEPT_SINGLE_VALUE_作为_数组)
私人列表订单;
在我的例子中,显示错误是因为当我使用Jackson库读取JSON文件时,我的JSON文件只包含1个对象。因此,它以“{”开头,以“}”结尾。但在读取它并将其存储在变量中时,我将其存储在数组对象中(在我的例子中,可能有多个对象)
因此,我在JSON文件的开头添加了“[”,在结尾添加了“]”,将其转换为一个对象数组,它工作得非常好,没有任何错误。这些天我遇到了同样的问题,也许更多的细节可能会对其他人有所帮助
我在寻找RESTAPI的一些安全指南,并与json数组进行了比较。查看链接了解详细信息,但基本上,您应该将它们包装在一个对象中,正如我们在本文问题中已经看到的那样
因此,不是:
[
{
"name": "order1"
},
{
"name": "order2"
}
]
我们最好总是这样做:
{
"data": [
{
"name": "order1"
},
{
"name": "order2"
}
]
}
当您执行GET时,这是非常直接的,但是如果您尝试发布/放置相同的json,则可能会给您带来一些麻烦
在我的例子中,我有不止一个GET是一个列表,还有不止一个POST/PUT将接收相同的json
因此,我最终要做的是为列表使用一个非常简单的包装器对象:
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;
}
}
反序列化仍然是默认的,只是使用de包装器对象:
@PostMapping("/resource")
public ResponseEntity<Void> setResources(@RequestBody Wrapper<ResourceDTO> wrappedResources) {
List<ResourceDTO> resources = wrappedResources.getData();
// your code here
return ResponseEntity
.ok()
.build();
}
@PostMapping(“/resource”)
公共响应属性setResources(@RequestBody wrappedResources){
List resources=wrappedResources.getData();
//你的代码在这里
返回响应性
.ok()
.build();
}
就这样!希望它能帮助别人
注意:使用SpringBoot 1.5.5.RELEASE进行测试。如上所述,以下操作将解决问题:mapper.configure(反序列化功能。接受单个值作为数组,true)代码>
但是,在我的例子中,提供程序执行[0..1]或[0..*]序列化,而不是作为一个bug,我无法强制执行修复。另一方面,它不想影响我的所有其他需要严格验证的情况下的严格映射程序
因此,我做了一个令人讨厌的Jackson 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());
}
@JsonProperty(value=“singleorliselement”,access=JsonProperty.access.WRITE\u ONLY)
公共关系
{
"data": [
{
"name": "order1"
},
{
"name": "order2"
}
]
}
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;
}
}
@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;
}
}
{
"data": [
{...}
]
}
@PostMapping("/resource")
public ResponseEntity<Void> setResources(@RequestBody Wrapper<ResourceDTO> wrappedResources) {
List<ResourceDTO> resources = wrappedResources.getData();
// your code here
return ResponseEntity
.ok()
.build();
}
@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());
}
@RequestBody List<String> ids
{
"ids": [
"1234",
"5678"
]
}
["1234", "5678"]