Java 使用 Spring 和 JsonTypeInfo 注释将 JSON 反序列化为多态对象模型
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/19239413/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
De-serializing JSON to polymorphic object model using Spring and JsonTypeInfo annotation
提问by grigori
I have the following object model in my Spring MVC (v3.2.0.RELEASE) web application:
我的 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 {}
When I serialise the Order class to JSON, I get the following result (which is exactly what I want):
当我将 Order 类序列化为 JSON 时,我得到以下结果(这正是我想要的):
{
"payment" : {
"creditCardPayment": {
...
}
}
Unfortunately, if I take the above JSON and try to de-serialise it back into my object model, I get the following exception:
不幸的是,如果我采用上述 JSON 并尝试将其反序列化回我的对象模型,则会出现以下异常:
Could not read JSON: Could not resolve type id 'creditCardPayment' into a subtype of [simple type, class Payment] at [Source: org.apache.catalina.connector.CoyoteInputStream@19629355; line: 1, column: 58] (through reference chain: Order["payment"]); nested exception is com.fasterxml.Hymanson.databind.JsonMappingException: Could not resolve type id 'creditCardPayment' into a subtype of [simple type, class Payment] at [Source: org.apache.catalina.connector.CoyoteInputStream@19629355; line: 1, column: 58] (through reference chain: Order["payment"])
无法读取 JSON:无法在 [来源:org.apache.catalina.connector.CoyoteInputStream@19629355;行:1,列:58](通过参考链:Order["payment"]);嵌套异常是 com.fasterxml.Hymanson.databind.JsonMappingException:无法将类型 ID 'creditCardPayment' 解析为 [来源:org.apache.catalina.connector.CoyoteInputStream@19629355; 处的 [简单类型,类付款] 的子类型;行:1,列:58](通过参考链:Order["payment"])
My application is configured via Spring JavaConf, as follows:
我的应用程序是通过 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 MappingHymanson2HttpMessageConverter mappingHymansonMessageConverter() {
MappingHymanson2HttpMessageConverter converter = new MappingHymanson2HttpMessageConverter();
converter.setObjectMapper(objectMapper());
return converter;
}
@Bean
public Jaxb2RootElementHttpMessageConverter jaxbMessageConverter() {
return new Jaxb2RootElementHttpMessageConverter();
}
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
converters.add(jaxbMessageConverter());
converters.add(mappingHymansonMessageConverter());
}
}
For testing, I have a controller with 2 methods, one returns an Order for HTTP GET request (this one works) and one that accepts an Order via a HTTP POST (this one fails), e.g.
为了进行测试,我有一个带有 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) {}
}
I have tried all suggestions from the various discussions on SO but so far had no luck. Can anyone spot what I'm doing wrong?
我已经尝试了关于 SO 的各种讨论中的所有建议,但到目前为止还没有运气。谁能发现我做错了什么?
采纳答案by ragnor
Try to register subtype using ObjectMapper.registerSubtypes
instead of using annotations
尝试使用ObjectMapper.registerSubtypes
而不是使用注释来注册子类型
回答by Rashmin H Gadhavi
The method registerSubtypes()
works!
方法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();
}
Note1: We use the Interface and the implementing classes. I fyou want Hymanson to de-serialize the classes as per their implementing classes, you have to register all of them using ObjectMapper's "registerSubtypes" method.
注1:我们使用接口和实现类。如果您希望 Hymanson 根据它们的实现类对类进行反序列化,则必须使用 ObjectMapper 的“registerSubtypes”方法注册所有这些类。
Note2: In addition you use, " @JsonTypeInfo(use=JsonTypeInfo.Id.NAME, include=JsonTypeInfo.As.PROPERTY, property="type")" as annotation with your Interface.
注意2:此外,您还使用“@JsonTypeInfo(use=JsonTypeInfo.Id.NAME, include=JsonTypeInfo.As.PROPERTY, property="type")”作为您的界面的注释。
You can also define the order of properties when mapper writes a value of your POJO as a json.
当映射器将 POJO 的值作为 json 写入时,您还可以定义属性的顺序。
This you can do using below annotation.
您可以使用以下注释来完成此操作。
@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();
........
}
Hope this helps!
希望这可以帮助!
回答by herrtim
Rashmin's answer worked, and I found an alternative way to avoid the com.fasterxml.Hymanson.databind.JsonMappingException: Could not resolve type id into a subtype of Blah
issue without needing to use registerSubtypes
. What you can do is add the following annotation to the parent class:
Rashmin 的回答奏效了,我找到了一种com.fasterxml.Hymanson.databind.JsonMappingException: Could not resolve type id into a subtype of Blah
无需使用registerSubtypes
. 您可以做的是在父类中添加以下注释:
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY, property = "type")
Note that the difference is JsonTypeInfo.Id.CLASS
instead of JsonTypeInfo.Id.NAME
. The downside is that the created JSON will contain the entire class name including the full namespace. The upside is that you don't have to worry about registering subtypes.
请注意,不同之处JsonTypeInfo.Id.CLASS
在于JsonTypeInfo.Id.NAME
。缺点是创建的 JSON 将包含整个类名,包括完整的命名空间。好处是您不必担心注册子类型。
回答by Aaron Cooley
I had a similar issue while working on a dropwizard based service. I don't fully understand why things didn't work for me in the same way that the dropwizard code works, but I know why the code in the original post doesn't work. @JsonSubTypes
wants an array of sub types, not a single value. So if you replace the line...
我在处理基于 dropwizard 的服务时遇到了类似的问题。我不完全理解为什么事情对我不起作用的方式与 dropwizard 代码的工作方式相同,但我知道为什么原始帖子中的代码不起作用。@JsonSubTypes
想要一个子类型数组,而不是单个值。因此,如果您更换线路...
@JsonSubTypes.Type(name = "creditCardPayment", value = CreditCardPayment.class)
with...
和...
@JsonSubTypes({ @JsonSubTypes.Type(name = "creditCardPayment", value = CreditCardPayment.class) })
I believe your code will work.
我相信你的代码会起作用。
For those that are having this same error message pop up, you may be having an issue with the subtypes being discovered. Try adding a line like the one above or looking for issue with the discovery of the classes that have the @JsonTypeName
tag in them.
对于那些弹出相同错误消息的人,您可能遇到了正在发现的子类型的问题。尝试添加类似上面的一行,或者寻找发现其中包含@JsonTypeName
标记的类的问题。
回答by dibs
Encountered the same error and used the equivalent of the below JSON (instead of CreditCardPayment
used my class name) as the input for deserializer and it worked:
遇到相同的错误并使用以下 JSON 的等效项(而不是CreditCardPayment
使用我的类名)作为反序列化器的输入并且它起作用了:
{
"type": "CreditCardPayment",
...
}
回答by Anand Rockzz
In my case I had added defaultImpl = SomeClass.class
to @JsonTypeInfo and was trying to convert it SomeClass2.class
就我而言,我已添加defaultImpl = SomeClass.class
到 @JsonTypeInfo 并试图将其转换为 SomeClass2.class