java 什么@JsonTypeInfo.ID 为 property = "type.id" 选择反序列化,JsonTypeInfo.Id.CUSTOM?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/25671806/
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
What @JsonTypeInfo.ID to choose for property = "type.id" for deserialization, JsonTypeInfo.Id.CUSTOM?
提问by Eric D. Johnson
So I have JSON that looks like this:
所以我有一个看起来像这样的 JSON:
{
"ActivityDisplayModel" : {
"name" : "lunch with friends",
"startTime" : "12:00:00",
"type" : {
"id" : "MEAL",
"description" : "Meal"
},
"complete" : false
}
}
I'm trying to find the way to get @JsonTypeInfo
to not be mad at me for having the type parameter inside the type
object. I've got this working before when the field type
was a String and not an object itself, but for later processing I need it as an object. I know the following doesn't work, and I'm guessing theres a way to use JsonTypeInfo.Id.CUSTOM, but after looking all over on the internet, no fully examples with JSON have come up. Also, if this is possible with an objectMapper setting, I'm all ears.
我试图找到一种方法,让@JsonTypeInfo
我不要因为在type
对象中包含类型参数而生我的气。当字段type
是字符串而不是对象本身时,我已经开始使用它了,但是为了以后的处理,我需要它作为对象。我知道以下不起作用,我猜有一种方法可以使用JsonTypeInfo.Id.CUSTOM,但是在互联网上查看了所有内容之后,没有完整的 JSON 示例出现。另外,如果使用 objectMapper 设置可以做到这一点,我会全力以赴。
/**
* My ActivityDisplayModel Abstract Class
*/
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "type.id")
@JsonSubTypes({
@JsonSubTypes.Type(value = MealDisplayModel.class, name = "MEAL"),
@JsonSubTypes.Type(value = EntertainmentDisplayModel.class, name = "ENTERTAINMENT")
})
public abstract class ActivityDisplayModel {
...
The above is essentially what I want to do, but of course I get an exception of:
以上基本上是我想要做的,但当然我有一个例外:
Could not read JSON: Could not resolve type id '{' into a subtype of [simple type, class ... .ActivityDisplayModel]
For such a simple problem of just looking one level deeper in the JSON, who would have thought it would have been so much trouble?
这么简单的问题,仅仅在 JSON 中深入一层,谁会想到会这么麻烦?
采纳答案by Micha? Ziober
I am not sure that you can do it with specifying inner property: type.id
. In my opinion you should change your JSON to simpler version. If you can not force your JSON supplier to change JSON schema you have to do it manually. Assume that your JSON looks like below:
我不知道,你可以指定内部属性做到这一点:type.id
。在我看来,您应该将 JSON 更改为更简单的版本。如果您不能强制您的 JSON 供应商更改 JSON 架构,您必须手动进行。假设您的 JSON 如下所示:
{
"activityDisplayModel": {
"name": "lunch with friends",
"type": {
"id": "MEAL",
"description": "Meal"
},
"complete": false
}
}
Below POJO classes fit to above JSON:
下面的 POJO 类适合上面的 JSON:
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "type")
@JsonSubTypes({
@JsonSubTypes.Type(value = MealDisplayModel.class, name = "MEAL"),
@JsonSubTypes.Type(value = EntertainmentDisplayModel.class, name = "ENTERTAINMENT")
})
abstract class ActivityDisplayModel {
protected String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return name;
}
}
class MealDisplayModel extends ActivityDisplayModel {
private boolean complete;
public boolean isComplete() {
return complete;
}
public void setComplete(boolean complete) {
this.complete = complete;
}
@Override
public String toString() {
return "MealDisplayModel [complete=" + complete + ", toString()=" + super.toString() + "]";
}
}
@JsonIgnoreProperties("complete")
class EntertainmentDisplayModel extends ActivityDisplayModel {
@Override
public String toString() {
return "EntertainmentDisplayModel [toString()=" + super.toString() + "]";
}
}
class Root {
private ActivityDisplayModel activityDisplayModel;
public ActivityDisplayModel getActivityDisplayModel() {
return activityDisplayModel;
}
public void setActivityDisplayModel(ActivityDisplayModel activityDisplayModel) {
this.activityDisplayModel = activityDisplayModel;
}
@Override
public String toString() {
return activityDisplayModel.toString();
}
}
Below script shows how you can parse above JSON:
下面的脚本显示了如何解析上面的 JSON:
ObjectMapper mapper = new ObjectMapper();
// Updated JSON in memory
ObjectNode rootNode = (ObjectNode)mapper.readTree(json);
ObjectNode activityDisplayModelNode = (ObjectNode)rootNode.path("activityDisplayModel");
JsonNode typeNode = activityDisplayModelNode.path("type");
activityDisplayModelNode.set("type", typeNode.path("id"));
System.out.println("Result: " + mapper.convertValue(rootNode, Root.class));
Above script prints:
以上脚本打印:
Result: MealDisplayModel [complete=false, toString()=lunch with friends]
Also see:
另见:
回答by Strumbles
I know it's been 3 years since the original question, but dot-nested properties are still not supported and maybe this will help someone out. I ended up creating a class NestedTypeResolver
so we can use the dot-syntax as expected. Simply add @JsonTypeResolver(NestedTypeResolver.class)
to any class with nested discriminators and the poster's original attempt will work:
我知道距离最初的问题已经过去 3 年了,但仍然不支持点嵌套属性,也许这会对某人有所帮助。我最终创建了一个类,NestedTypeResolver
以便我们可以按预期使用点语法。只需添加@JsonTypeResolver(NestedTypeResolver.class)
到具有嵌套鉴别器的任何类,发布者的原始尝试将起作用:
/**
* My ActivityDisplayModel Abstract Class
*/
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "type.id")
@JsonSubTypes({
@JsonSubTypes.Type(value = MealDisplayModel.class, name = "MEAL"),
@JsonSubTypes.Type(value = EntertainmentDisplayModel.class, name = "ENTERTAINMENT")
})
@JsonTypeResolver(NestedTypeResolver.class)
public abstract class ActivityDisplayModel {
NestedTypeResolver:
嵌套类型解析器:
/**
* Allows using nested "dot" dyntax for type discriminators. To use, annotate class with @JsonTypeResolver(NestedTypeResolver.class)
*/
public class NestedTypeResolver extends StdTypeResolverBuilder {
@Override
public TypeDeserializer buildTypeDeserializer(DeserializationConfig config, JavaType baseType,
Collection<NamedType> subtypes) {
//Copied this code from parent class, StdTypeResolverBuilder with same method name
TypeIdResolver idRes = idResolver(config, baseType, subtypes, false, true);
return new NestedTypeDeserializer(baseType, idRes, _typeProperty, _typeIdVisible,
null, _includeAs);
}
}
All the heavy work is done in here, NestedTypeDeserializer:
所有繁重的工作都在这里完成,NestedTypeDeserializer:
/**
* Heavy work to support {@link NestedTypeResolver}
*/
public class NestedTypeDeserializer extends AsPropertyTypeDeserializer {
private static final Logger LOGGER = LoggerFactory.getLogger(NestedTypeDeserializer.class);
public NestedTypeDeserializer(JavaType bt,
TypeIdResolver idRes, String typePropertyName, boolean typeIdVisible,
JavaType defaultImpl) {
super(bt, idRes, typePropertyName, typeIdVisible, defaultImpl);
}
public NestedTypeDeserializer(JavaType bt, TypeIdResolver idRes, String typePropertyName, boolean typeIdVisible,
JavaType defaultImpl, JsonTypeInfo.As inclusion) {
super(bt, idRes, typePropertyName, typeIdVisible, defaultImpl, inclusion);
}
public NestedTypeDeserializer(AsPropertyTypeDeserializer src, BeanProperty property) {
super(src, property);
}
@Override
public TypeDeserializer forProperty(BeanProperty prop) {
return (prop == _property) ? this : new NestedTypeDeserializer(this, prop);
}
@Override
public Object deserializeTypedFromObject(JsonParser p, DeserializationContext ctxt) throws IOException {
JsonNode originalNode = p.readValueAsTree();
JsonNode node = originalNode;
//_typePropertyName is the dot separated value of "property" in @JsonTypeInfo
LOGGER.debug("Searching for type discriminator [{}]...", _typePropertyName);
for (String property : _typePropertyName.split("\.")) { //traverse down any nested properties
JsonNode nestedProp = node.get(property);
if (nestedProp == null) {
ctxt.reportWrongTokenException(p, JsonToken.FIELD_NAME,
"missing property '" + _typePropertyName + "' that is to contain type id (for class "
+ baseTypeName() + ")");
return null;
}
node = nestedProp;
}
LOGGER.debug("Found [{}] with value [{}]", _typePropertyName, node.asText());
JsonDeserializer<Object> deser = _findDeserializer(ctxt, "" + node.asText());
//Since JsonParser is a forward-only operation and finding the "type" discriminator advanced the pointer, we need to reset it
//Got clues from https://www.dilipkumarg.com/dynamic-polymorphic-type-handling-Hymanson/
JsonParser jsonParser = new TreeTraversingParser(originalNode, p.getCodec());
if (jsonParser.getCurrentToken() == null) {
jsonParser.nextToken();
}
return deser.deserialize(jsonParser, ctxt);
}
}
Disclaimer: we've been using this for a month with Hymanson 2.8.10 and have had no issues, but we had to go deep into the Hymanson source code weeds to accomplish it, so YMMV. Hopefully Hymanson will allow this out-of-the-box someday so we dont need these workarounds.
免责声明:我们已经在 Jackson 2.8.10 上使用了一个月并且没有任何问题,但是我们必须深入研究 Hymanson 源代码杂草才能完成它,所以 YMMV。希望Hyman逊有一天会允许这种开箱即用的方式,这样我们就不需要这些变通方法了。