programing

java.util 인스턴스를 역직렬화할 수 없습니다.START_OBJECT 토큰에서 ArrayList가 벗어났습니다.

minecode 2022. 8. 25. 23:04
반응형

java.util 인스턴스를 역직렬화할 수 없습니다.START_OBJECT 토큰에서 ArrayList가 벗어났습니다.

List은 다음과 같습니다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();
    }
}

" "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() + "]";
    }
}

하지만 예외는 있습니다.

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」으로수 없습니다.이것은 디폴트로는 디시리얼라이즈 할 수 없습니다.Collection배열이 JSON .

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

정확한 역직렬화 프로세스를 제어하고 있지 않기 때문에(RestEasy가 제어한다) 첫 번째 옵션은 단순히 JSON을 인스톨 하는 것입니다.String다음으로 역직렬화 프로세스를 제어합니다.

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

직접 하지 않아도 된다는 편리함을 조금이나마 덜 수 있지만, 문제를 쉽게 해결할 수 있습니다.

JSON을 변경할 수 없는 경우 다른 옵션은 JSON 입력 구조에 맞는 래퍼를 작성하고 대신 래퍼를 사용하는 것입니다.Collection<COrder>

이게 도움이 됐으면 좋겠다.

JSON 문서 대신 다음과 같이 ObjectMapper 개체를 업데이트할 수 있습니다.

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

이 조작은 유효합니다.

이 문제는 단일 요소가 포함된 목록을 JsonNode가 아닌 JsonArray로 읽으려고 할 때 발생할 수 있습니다.

반환된 목록에 단일 요소가 포함되어 있는지 여부를 확실히 알 수 없기 때문에(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 {
    ...
}

Eugen의하여, 당신은 Eugen이 된 포장지 를 만들면 이 할 수 .Collection<COrder>멤버 변수로 사용합니다.에 의해, 의 「Discounting」을 할 수 .CollectionPOJO의 멤버 변수 내의 데이터와 API 요청에서 찾고 있는 JSON을 생성합니다.

예제:

public class ApiRequest {

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

   // getters
}

다음 매개 .COrderRestService.postOrder()이 되다ApiRequest「가 아닌 「Collection<COrder>.

와 같이 다음과 를 해결할 수 .mapper.configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true);

단, 이 경우 프로바이더는 [0..1]또는 [0..]를 실행했습니다.*] 시리얼라이제이션은 버그로 인한 것이 아니라 수정을 강제할 수 없었습니다.한편, 엄격하게 검증할 필요가 있는 다른 모든 케이스에 대해서는, 나의 엄격한 맵퍼에 영향을 주고 싶지 않았습니다.

그래서 Jackson DISTY 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());
  }

나도 요즘 같은 문제에 부딪혔는데 좀 더 자세한 내용이 다른 사람에게 도움이 될지도 몰라.

REST API에 대한 보안 가이드라인을 찾다가 json 어레이에 관한 매우 흥미로운 문제를 발견했습니다.자세한 내용은 링크를 참조해 주세요.기본적으로 이 투고 질문에서 이미 살펴본 바와 같이 오브젝트 내에서 그것들을 랩해야 합니다.

즉, 다음과 같은 것이 아니라

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

항상 다음을 수행하는 것이 좋습니다.

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

이것은 GET을 실행할 때 매우 간단하지만, 같은 json을 POST/PUT하려고 하면 문제가 발생할 수 있습니다.

제 경우 목록인 GET과 동일한 json을 받는 POST/PUT이 여러 개 있었습니다.

따라서 목록에는 매우 간단한 Wrapper 객체를 사용합니다.

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를 사용하여 작성되었습니다.

@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": [
      {...}
    ]
  }

de Wrapper Object를 사용하는 것만으로는 디폴트입니다.

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

바로 그거였어!도움이 됐으면 좋겠는데

주의: Spring Boot 1.5.5로 테스트 완료.해방.

@JsonFormat(with = JsonFormat.Feature.ACCEPT_SINGLE_VALUE_AS_ARRAY)
private List< COrder > orders;

WAY와 오랫동안 씨름한 끝에, 여기 아주 쉬운 해결책이 있습니다.

컨트롤러가 찾고 있던 것은

@RequestBody List<String> ids

그리고 요청 주체는

{
    "ids": [
        "1234",
        "5678"
     ]
}

해결책은 단순히 몸을 바꿔서

["1234", "5678"]

네, 그렇게 쉬워요

Spring 프레임워크를 사용하여 작성한 REST API에서 이 문제가 발생하였습니다.(응답이 JSON이 되도록) @ResponseBody 주석을 추가하면 해결되었습니다.

일반적으로 JSON 노드를 Java 객체의 노드에 매핑하는 데 문제가 있을 때 이 문제가 발생합니다.스웨거에서는 노드가 Type array로 정의되어 있고 JSON 오브젝트에 요소가1개밖에 없기 때문에 시스템이 하나의 요소 리스트를 어레이에 매핑하는 데 어려움을 겪고 있었기 때문에 같은 문제에 직면했습니다.

스웨거에서 요소는 다음과 같이 정의되었습니다.

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

해야 할 때마침

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

★★★★★★★★★★★★★★★★★.TestNew.

Dto response = softConvertValue(jsonData, Dto.class);


     public static <T> T softConvertValue(Object fromValue, Class<T> toValueType) 
        {
            ObjectMapper objMapper = new ObjectMapper();
            return objMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, 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);

NULL(No content POST)입니다.설명한 와 같이 그 에 유효한이 포함되어 있지 않기 수 없었기 입니다(OP의 제한사항).요구에 유효한 JSON이 포함되어 있지 않기 때문에 자동으로 애플리케이션/json 요구로 특정할 수 없었기 때문입니다(서버상의 제한).consumes = "application/json"유효한 JSON 요구는 다음과 같습니다.엔티티에 null body 및 json 헤더를 명시적으로 채우는 문제를 해결했습니다.

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

제 경우 잭슨 라이브러리를 사용하여 JSON 파일을 읽었을 때 JSON 파일에 오브젝트가 1개밖에 포함되어 있지 않았기 때문에 오류가 발생하고 있었습니다.따라서 "{"로 시작하여 "}"로 끝납니다.그러나 읽어서 변수에 저장하는 동안 Array 오브젝트에 저장하고 있었습니다(내 경우처럼 여러 오브젝트가 있을 수 있습니다).

따라서 JSON 파일의 선두에 ""를, 마지막에 ""를 추가하여 오브젝트 배열로 변환하면 오류 없이 완벽하게 동작합니다.

같은 문제로 작업 중이었는데, 한 번에 여러 개의 레코드를 저장하고 싶었습니다.

많은 연구 끝에, 는 즉시 효과가 있는 쉬운 해결책을 찾아냈다.

문제 코드를 해결하기 전에:

컨트롤러 방식

   @PostMapping("/addList")
public ResponseEntity<List<Alarm>> saveAlarmList(@RequestBody List<AlarmDTO> dtos) {
    return ResponseEntity.ok(alarmService.saveAlarmList(dtos));
}

서비스 방법

 public List<Alarm> saveAlarmList(List<AlarmDTO> dtos) {

    List<Alarm> alarmList = new ArrayList<>();
    for (AlarmDTO dto: dtos) {
        Alarm newAlarm = AlarmDtoMapper.map(dto);
        alarmList.add(newAlarm);
    }

    return alarmRepository.saveAll(alarmList);
}

엔티티 클래스

   public class Alarm{

@Id
@Basic(optional = false)
@NotNull
@Column(name = "alarm_id")
private Long alarmId;

@Basic(optional = false)
@NotNull
@Size(min = 1, max = 100)
@Column(name = "alarm_name")
private String alarmName;

}

우체부 오브젝트

   [
    {
      "alarmId":"55",
      "alarmName":"fgdffg",
    },
    {
      "alarmId":"77788",
      "alarmName":"hjjjjfk",
    }
   ]

및 다른 형식

      { 
        "list":   [
           {
             "alarmId":"55",
             "alarmName":"fgdffg",
           },
           {
              "alarmId":"77788",
              "alarmName":"hjjjjfk",
           }
         ]
        }

두 방법 모두 시리얼화/디시리얼화 오류가 발생하고 있습니다.

다음으로 엔티티 클래스에서 Serializable 인터페이스를 구현했습니다.

엔티티 클래스

  public class Alarm implements Serializable
  {
      @Id
      @Basic(optional = false)
      @NotNull
      @Column(name = "alarm_id")
      private Long alarmId;

      @Basic(optional = false)
      @NotNull
      @Size(min = 1, max = 100)
      @Column(name = "alarm_name")
      private String alarmName;
   }

이 수정 후 우체부로부터의 첫 번째 유형의 요청 객체가 작동했습니다.

언급URL : https://stackoverflow.com/questions/20837856/can-not-deserialize-instance-of-java-util-arraylist-out-of-start-object-token

반응형