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

Java 使用Spring和JsonTypeInfo注释将JSON反序列化为多态对象模型,java,json,spring,jackson,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) pub

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

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

无法读取JSON:无法解析类型id“creditCardPayment” 在[Source: 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());
    }
}

到目前为止,我已经尝试了各种讨论中的所有建议,但到目前为止都没有成功。有人能发现我做错了什么吗?

尝试使用
对象映射器注册子类型。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:我们使用接口和实现类。如果希望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();
    ........
}
@JsonPropertyOrder({“类型”、“crs”、“版本”、“功能”})
公共类GeoJson{
私有字符串类型=“FeatureCollection”;
私有列表功能;
私有字符串版本=“1.0.0”;
专用CRS=新CRS();
........
}

希望这有帮助

Rashmin的答案奏效了,我找到了另一种避免
com.fasterxml.jackson.databind.JsonMappingException的方法:在不需要使用
registerSubtypes
的情况下,无法将类型id解析为Blah问题的子类型。您可以向父类添加以下注释:

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

请注意,区别在于
JsonTypeInfo.Id.CLASS
,而不是
JsonTypeInfo.Id.NAME
。缺点是,创建的JSON将包含整个类名,包括完整的命名空间。好处是您不必担心注册子类型。

我在使用基于dropwizard的服务时遇到了类似的问题。我不完全理解为什么dropwizard代码的工作方式与dropwizard代码的工作方式不同,但我知道为什么原始帖子中的代码不起作用
@JsonSubTypes
需要子类型数组,而不是单个值。所以如果你换掉这条线

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

@JsonSubTypes({ @JsonSubTypes.Type(name = "creditCardPayment", value = CreditCardPayment.class) })
我相信你的代码会起作用


对于那些弹出相同错误消息的用户,您可能发现子类型有问题。尝试添加一行类似于上面的行,或者查找发现包含
@JsonTypeName
标记的类时出现的问题。

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

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

在我的例子中,我已经将
defaultImpl=SomeClass.class
添加到@JsonTypeInfo中,并试图将其转换为SomeClass2.class

,效果很好!我从
Payment
界面中删除了
@JsonSubTypes.Type(name=“creditCardPayment”,value=creditCardPayment.class)
,并添加了
objectMapper.registerSubtypes(creditCardPayment.class)
AppWebConf
类中。令人失望的是,注释没有起作用,因为我想保持配置的通用性,但至少它让我克服了这个问题。谢谢。我也遇到了同样的问题,上面的解决方案没有帮助。代码中的解释可能有用。对不起,我错过了。因此,让我用更多的功能重新完成整个过程。这给了我一个错误:
,原因是:com.fasterxml.jackson.databind.JsonMappingException:意外的标记(END_OBJECT),预期的字段名称:缺少属性“type”,该属性将在以下位置包含类型id(对于类com.microstar.tap3.contract.api.ContractObject)[来源:org.glassfish.jersey.message.internal.ReaderInterceptorExecutor$UnCloseableInputStream@5699c6f9;行:1,列:1023](通过引用链:java.util.ArrayList[0])
@JsonSubTypes({ @JsonSubTypes.Type(name = "creditCardPayment", value = CreditCardPayment.class) })
{
    "type": "CreditCardPayment", 
    ...
}