복붙노트

[SPRING] Spring과 JsonTypeInfo 어노테이션을 사용하여 JSON을 다형성 객체 모델로 역 직렬화

SPRING

Spring과 JsonTypeInfo 어노테이션을 사용하여 JSON을 다형성 객체 모델로 역 직렬화

내 봄 MVC (v3.2.0.RELEASE) 웹 응용 프로그램에 다음과 같은 개체 모델이 있습니다.

public class Order {
  private Payment payment;
}

@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = As.WRAPPER_OBJECT)
@JsonSubTypes.Type(name = "creditCardPayment", value = CreditCardPayment.class)
public interface Payment {}


@JsonTypeName("creditCardPayment")
public class CreditCardPayment implements Payment {}

Order 클래스를 JSON으로 serialize 할 때 다음 결과를 얻는다.

{
  "payment" : {
    "creditCardPayment": {
      ...
    } 
}

불행히도 위의 JSON을 가져 와서 객체 모델에 다시 직렬화하려고하면 다음 예외가 발생합니다.

내 응용 프로그램은 다음과 같이 Spring JavaConf를 통해 구성됩니다.

@Configuration
@EnableWebMvc
public class AppWebConf extends WebMvcConfigurerAdapter {

    @Bean
    public ObjectMapper objectMapper() {
        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.setSerializationInclusion(Include.NON_NULL);
        objectMapper.configure(MapperFeature.DEFAULT_VIEW_INCLUSION, false);
        return objectMapper;
    }

    @Bean
    public MappingJackson2HttpMessageConverter mappingJacksonMessageConverter() {
        MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
        converter.setObjectMapper(objectMapper());
        return converter;
    }

    @Bean
    public Jaxb2RootElementHttpMessageConverter jaxbMessageConverter() {
        return new Jaxb2RootElementHttpMessageConverter();
    }

    @Override
    public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
        converters.add(jaxbMessageConverter());
        converters.add(mappingJacksonMessageConverter());
    }
}

테스트를 위해 컨트롤러에 두 가지 메소드가 있습니다. 하나는 HTTP GET 요청에 대한 주문 (이 하나는 작동)과 HTTP POST (이 중 하나는 실패)를 통해 주문을 수락하는 것입니다.

@Controller
public class TestController {

    @ResponseBody
    @RequestMapping(value = "/test", method = RequestMethod.GET)
    public Order getTest() {}

    @RequestMapping(value = "/test", method = RequestMethod.POST)
    public void postTest(@RequestBody order) {}

}

나는 SO에 대한 다양한 토론에서 모든 제안을 시도했지만 지금까지 운이 없었습니다. 아무도 내가 잘못하고있는 것을 발견 할 수 있습니까?

해결법

  1. ==============================

    1.주석을 사용하는 대신 ObjectMapper.registerSubtypes를 사용하여 하위 유형을 등록하십시오.

    주석을 사용하는 대신 ObjectMapper.registerSubtypes를 사용하여 하위 유형을 등록하십시오.

  2. ==============================

    2.registerSubtypes () 메서드는 작동합니다!

    registerSubtypes () 메서드는 작동합니다!

    @JsonTypeInfo(use=JsonTypeInfo.Id.NAME, include=JsonTypeInfo.As.PROPERTY, property="type")
    public interface Geometry {
      //...
    }
    
    public class Point implements Geometry{
      //...
    }
    public class Polygon implements Geometry{
      //...
    }
    public class LineString implements Geometry{
      //...
    }
    
    GeoJson geojson= null;
    ObjectMapper mapper = new ObjectMapper();
    mapper.disable(DeserializationConfig.Feature.FAIL_ON_UNKNOWN_PROPERTIES);
    mapper.registerSubtypes(Polygon.class,LineString.class,Point.class);
    
    try {
        geojson=mapper.readValue(source, GeoJson.class);            
    } catch (IOException e) {
        e.printStackTrace();
    }
    

    참고 1 : 우리는 인터페이스와 구현 클래스를 사용합니다. 잭슨이 구현 클래스에 따라 클래스를 비 직렬화하려면 ObjectMapper의 "registerSubtypes"메소드를 사용하여 클래스 클래스를 모두 등록해야한다.

    주 2 : 또한 인터페이스에서 주석으로 @JsonTypeInfo (use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "type") "를 사용합니다.

    매퍼가 POJO의 값을 JSON으로 쓸 때 속성의 순서를 정의 할 수도 있습니다.

    이것은 아래 주석을 사용하여 수행 할 수 있습니다.

    @JsonPropertyOrder({"type","crs","version","features"})
    public class GeoJson {
    
        private String type="FeatureCollection";
        private List<Feature> features;
        private String version="1.0.0";
        private CRS crs = new CRS();
        ........
    }
    

    희망이 도움이!

  3. ==============================

    3.dropwizard 기반 서비스에서 작업하는 동안 비슷한 문제가 발생했습니다. dropwizard 코드가 작동하는 것과 같은 방식으로 문제가 해결되지 않는 이유를 완전히 이해하지 못하고 있지만 원래 게시물의 코드가 작동하지 않는 이유를 알고 있습니다. @JsonSubTypes는 단일 값이 아닌 하위 유형의 배열을 원합니다. 선을 대체하면 ...

    dropwizard 기반 서비스에서 작업하는 동안 비슷한 문제가 발생했습니다. dropwizard 코드가 작동하는 것과 같은 방식으로 문제가 해결되지 않는 이유를 완전히 이해하지 못하고 있지만 원래 게시물의 코드가 작동하지 않는 이유를 알고 있습니다. @JsonSubTypes는 단일 값이 아닌 하위 유형의 배열을 원합니다. 선을 대체하면 ...

    @JsonSubTypes.Type(name = "creditCardPayment", value = CreditCardPayment.class)
    

    와...

    @JsonSubTypes({ @JsonSubTypes.Type(name = "creditCardPayment", value = CreditCardPayment.class) })
    

    나는 당신의 코드가 작동한다고 믿는다.

    이 같은 오류 메시지가 나타나는 사람들은 발견 된 하위 유형에 문제가있을 수 있습니다. 위와 같은 행을 추가하거나 @JsonTypeName 태그가있는 클래스 발견과 관련된 문제점을 찾으십시오.

  4. ==============================

    4.Rashmin의 대답이 효과가 있었고 com.fasterxml.jackson.databind.JsonMappingException을 피할 수있는 다른 방법을 발견했습니다. registerSubtypes를 사용할 필요없이 유형 ID를 Blah 문제의 하위 유형으로 해석 할 수 없습니다. 부모 클래스에 다음 주석을 추가하면됩니다.

    Rashmin의 대답이 효과가 있었고 com.fasterxml.jackson.databind.JsonMappingException을 피할 수있는 다른 방법을 발견했습니다. registerSubtypes를 사용할 필요없이 유형 ID를 Blah 문제의 하위 유형으로 해석 할 수 없습니다. 부모 클래스에 다음 주석을 추가하면됩니다.

    @JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY, property = "type")
    

    차이점은 JsonTypeInfo.Id.NAME 대신 JsonTypeInfo.Id.CLASS입니다. 단점은 생성 된 JSON에 전체 네임 스페이스를 포함한 전체 클래스 이름이 포함된다는 것입니다. 단점은 하위 유형 등록에 대해 걱정할 필요가 없다는 것입니다.

  5. ==============================

    5.제 경우에는 defaultImpl = SomeClass.class를 @JsonTypeInfo에 추가하고 SomeClass2.class로 변환하려고했습니다.

    제 경우에는 defaultImpl = SomeClass.class를 @JsonTypeInfo에 추가하고 SomeClass2.class로 변환하려고했습니다.

  6. ==============================

    6.동일한 오류가 발생하여 deserializer의 입력으로 아래 JSON (CreditCardPayment 대신 클래스 이름 사용) 대신 동일한 값을 사용했습니다.

    동일한 오류가 발생하여 deserializer의 입력으로 아래 JSON (CreditCardPayment 대신 클래스 이름 사용) 대신 동일한 값을 사용했습니다.

    {
        "type": "CreditCardPayment", 
        ...
    }
    
  7. from https://stackoverflow.com/questions/19239413/de-serializing-json-to-polymorphic-object-model-using-spring-and-jsontypeinfo-an by cc-by-sa and MIT license