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

时间:2013-10-08 04:57:55

标签: java json spring jackson

我的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](通过参考链:订单[“付款”]);嵌套   异常是com.fasterxml.jackson.databind.JsonMappingException:   无法将类型ID“creditCardPayment”解析为子类型   [简单类型,班级付款]在[来源:   org.apache.catalina.connector.CoyoteInputStream@19629355;行:1,   专栏:58](通过参考链:订单[“付款”])

我的应用程序是通过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的各种讨论的所有建议,但到目前为止没有运气。谁能发现我做错了什么?

6 个答案:

答案 0 :(得分:11)

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

答案 1 :(得分:5)

方法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:我们使用Interface和实现类。我希望jackson根据它们的实现类对类进行反序列化,你必须使用ObjectMapper的“registerSubtypes”方法注册所有类。

注意2:此外,您使用“@JsonTypeInfo(use = JsonTypeInfo.Id.NAME,include = JsonTypeInfo.As.PROPERTY,property =”type“)”作为您的界面的注释。

当mapper将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();
    ........
}

希望这有帮助!

答案 2 :(得分:3)

我在处理基于dropwizard的服务时遇到了类似的问题。我不完全理解为什么事情对我来说不像dropwizard代码那样有用,但我知道为什么原帖中的代码不起作用。 @JsonSubTypes想要一个子类型数组,而不是单个值。所以,如果你更换线......

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

...与

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

我相信你的代码会有用。

对于那些弹出相同错误消息的人,您可能遇到了被发现的子类型的问题。尝试添加类似上面的一行,或者通过发现其中包含@JsonTypeName标记的类来查找问题。

答案 3 :(得分:0)

Rashmin的回答有效,我找到了另一种避免com.fasterxml.jackson.databind.JsonMappingException: Could not resolve type id into a subtype of Blah问题而无需使用registerSubtypes的方法。您可以做的是将以下注释添加到父类:

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

请注意,差异为JsonTypeInfo.Id.CLASS而非JsonTypeInfo.Id.NAME。缺点是创建的JSON将包含整个类名,包括完整的名称空间。好处是你不必担心注册子类型。

答案 4 :(得分:0)

遇到相同的错误并使用下面的JSON(而不是CreditCardPayment使用我的类名称)作为反序列化器的输入并且它有效:

{
    "type": "CreditCardPayment", 
    ...
}

答案 5 :(得分:0)

在我的情况下,我已将defaultImpl = SomeClass.class添加到@JsonTypeInfo并尝试将其转换为SomeClass2.class