使用Spring和JsonTypeInfo注释将JSON反序列化为多态对象模型

2022-09-01 08:58:30

我在Spring MVC(v3.2.0.RELEASE)Web应用程序中有以下对象模型:

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 时,我得到以下结果(这正是我想要的):

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

不幸的是,如果我采用上述JSON并尝试将其反序列化回我的对象模型,则会出现以下异常:

无法读取 JSON:无法将类型 id 'creditCardPayment' 解析为 [来源:org.apache.catalina.connector.CoyoteInputStream@19629355;行:1,列:58] 的 [简单类型,类付款] 的子类型(通过参考链:Order[“付款”]);嵌套的异常是 com.fasterxml.jackson.databind.JsonMappingException:无法将类型 id 'creditCardPayment' 解析为 [简单类型,类付款] 的子类型 [来源:org.apache.catalina.connector.CoyoteInputStream@19629355;行:1,列:58](通过引用链:Order[“付款”])

我的应用程序是通过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());
    }
}

对于测试,我有一个具有2个方法的控制器,一个返回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

尝试使用而不是使用注记来注册子类型ObjectMapper.registerSubtypes


答案 2

该方法有效!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:我们使用接口和实现类。我希望jackson按照它们的实现类对类进行反序列化,你必须使用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();
    ........
}

希望这有帮助!