【问题标题】:De-serializing JSON to polymorphic object model using Spring and JsonTypeInfo annotation使用 Spring 和 JsonTypeInfo 注释将 JSON 反序列化为多态对象模型
【发布时间】:2013-10-14 21:16:54
【问题描述】:

我的 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' 在 [Source: org.apache.catalina.connector.CoyoteInputStream@19629355;线:1, 列:58](通过引用链:Order["payment"]);嵌套的 例外是 com.fasterxml.jackson.databind.JsonMappingException: 无法将类型 ID“creditCardPayment”解析为 [简单类型,支付类] [来源: org.apache.catalina.connector.CoyoteInputStream@19629355;线:1, 列:58](通过参考链:Order["payment"])

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

【问题讨论】:

    标签: java json spring jackson


    【解决方案1】:

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

    【讨论】:

    • 成功了!我从Payment 接口中删除了@JsonSubTypes.Type(name = "creditCardPayment", value = CreditCardPayment.class),并在AppWebConf 类中添加了objectMapper.registerSubtypes(CreditCardPayment.class);。令人失望的是注释不起作用,因为我想保持我的配置通用,但至少它让我克服了这个问题。谢谢。
    • 我遇到了同样的问题,上面的解决方案没有帮助。
    【解决方案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")”作为接口的注释。

    您还可以在 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();
        ........
    }
    

    希望这会有所帮助!

    【讨论】:

    • 附上代码的解释可能很有用
    • 对不起,我错过了。所以让我用更多的功能重新做整个事情。
    【解决方案3】:

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

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

    与...

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

    我相信你的代码会起作用。

    对于那些弹出相同错误消息的用户,您可能遇到了与正在发现的子类型有关的问题。尝试像上面那样添加一行,或者寻找发现其中包含 @JsonTypeName 标记的类的问题。

    【讨论】:

      【解决方案4】:

      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 将包含整个类名,包括完整的命名空间。好处是您不必担心注册子类型。

      【讨论】:

      • 这给了我这个错误:Caused by: com.fasterxml.jackson.databind.JsonMappingException: Unexpected token (END_OBJECT), expected FIELD_NAME: missing property 'type' that is to contain type id (for class com.microstar.tap3.contract.api.ContractObject) at [Source: org.glassfish.jersey.message.internal.ReaderInterceptorExecutor$UnCloseableInputStream@5699c6f9; line: 1, column: 1023] (through reference chain: java.util.ArrayList[0])
      【解决方案5】:

      就我而言,我已将 defaultImpl = SomeClass.class 添加到 @JsonTypeInfo 并尝试将其转换为 SomeClass2.class

      【讨论】:

        【解决方案6】:

        遇到了同样的错误,并使用以下 JSON 的等价物(而不是 CreditCardPayment 使用我的类名)作为反序列化器的输入并且它起作用了:

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

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2016-03-09
          • 2020-12-02
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-05-30
          • 1970-01-01
          相关资源
          最近更新 更多