Jackson polymorphic deserialization with type property that is nested in object(具有嵌套在对象中的类型属性的杰克逊多态反序列化)
问题描述
我正在尝试找到一种方法来使用 jackson 的多态反序列化功能,它会根据嵌套在标题/控件对象中的属性反序列化我的对象:
I'm trying to find out a way to use the polymorphic deserialization feature of jackson in a way that it will deserialize my object based on a property that is nested in header/control object:
JSON 1 - 类别 1:
JSON 1 - CATEGORY1:
{
"id":"someId",
"header":{
"category":"CATEGORY1",
"somOtherProperty":"someValue"
}
"nextField":"nextValue",
...
}
JSON 2 - 类别 2
JSON 2 - CATEGORY2
{
"id":"someId",
"header":{
"category":"CATEGORY2",
"somOtherProperty":"someValue"
}
"nextField":"nextValue",
...
}
父类(类似这样的注释)
Parent Class (annotations something like this)
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "category")
@JsonSubTypes({
@Type(value = Category1Class.class, name = "CATEGORY1"),
@Type(value = Category2Class.class, name = "CATEGORY2") })
public class ParentClass{
private Header header;
private String nextField;
...
}
public class Header{
private String category;
private String somOtherProperty;
...
}
儿童班
@JsonTypeName("CATEGORY1")
public class Category1Class extends ParentClass{
...
}
@JsonTypeName("CATEGORY2")
public class Category2Class extends ParentClass{
...
}
jackson 中是否有开箱即用的功能可以让我进行这种反序列化,或者我错过了什么?
Is there an out of the box functionality in jackson that would enable me to do this kind of deserialization or am I missing something?
推荐答案
如果你看Jackson Api AsPropertyTypeDeserializer
是负责使用属性识别子类型的类.如果您查看该类,则有一个名为 deserializeTypedFromObject
的方法,它将使用 JsonTypeIdResolver
识别子类.我们可以扩展这个类并覆盖方法 deserializeTypedFromObject
和 forProperty
.
If you look at the Jackson Api AsPropertyTypeDeserializer
is the class responsible for sub type identification using property. If you look at that class there is a method called deserializeTypedFromObject
which will identify the subclass using JsonTypeIdResolver
. We can extend this class and override methods deserializeTypedFromObject
and forProperty
.
package com.dilipkumarg.tutorials.dynamicsubtype;
import java.io.IOException;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.BeanProperty;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.jsontype.TypeDeserializer;
import com.fasterxml.jackson.databind.jsontype.TypeIdResolver;
import com.fasterxml.jackson.databind.jsontype.impl.AsPropertyTypeDeserializer;
import com.fasterxml.jackson.databind.node.TreeTraversingParser;
import com.fasterxml.jackson.databind.type.SimpleType;
public class CustomTypeDeserializer extends AsPropertyTypeDeserializer {
public CustomTypeDeserializer(
final JavaType bt, final TypeIdResolver idRes,
final String typePropertyName, final boolean typeIdVisible, final Class<?> defaultImpl) {
super(bt, idRes, typePropertyName, typeIdVisible, defaultImpl);
}
public CustomTypeDeserializer(
final AsPropertyTypeDeserializer src, final BeanProperty property) {
super(src, property);
}
@Override
public TypeDeserializer forProperty(
final BeanProperty prop) {
return (prop == _property) ? this : new CustomTypeDeserializer(this, prop);
}
@Override
public Object deserializeTypedFromObject(
final JsonParser jp, final DeserializationContext ctxt) throws IOException {
JsonNode node = jp.readValueAsTree();
Class<?> subType = findSubType(node);
JavaType type = SimpleType.construct(subType);
JsonParser jsonParser = new TreeTraversingParser(node, jp.getCodec());
if (jsonParser.getCurrentToken() == null) {
jsonParser.nextToken();
}
/* 16-Dec-2010, tatu: Since nominal type we get here has no (generic) type parameters,
* we actually now need to explicitly narrow from base type (which may have parameterization)
* using raw type.
*
* One complication, though; can not change 'type class' (simple type to container); otherwise
* we may try to narrow a SimpleType (Object.class) into MapType (Map.class), losing actual
* type in process (getting SimpleType of Map.class which will not work as expected)
*/
if (_baseType != null && _baseType.getClass() == type.getClass()) {
type = _baseType.narrowBy(type.getRawClass());
}
JsonDeserializer<Object> deser = ctxt.findContextualValueDeserializer(type, _property);
return deser.deserialize(jsonParser, ctxt);
}
protected Class<?> findSubType(JsonNode node) {
Class<? extends ParentClass> subType = null;
String cat = node.get("header").get("category").asText();
if (cat.equals("CATEGORY1")) {
subType = Category1Class.class;
} else if (cat.equals("CATEGORY2")) {
subType = Category2Class.class;
}
return subType;
}
}
在扩展类中,我们使用 idResolver 绕过子类型识别,而是使用 header
字段的 category
字段动态识别.
我们需要 TypeResolverBuilder
来创建新的 CustomTypeDeserializer
实例.
In extended class we bypassed sub type identification using idResolver instead we are identifying dynamically with category
field of header
field.
We need TypeResolverBuilder
to create new CustomTypeDeserializer
instance.
package com.dilipkumarg.tutorials.dynamicsubtype;
import java.util.Collection;
import com.fasterxml.jackson.databind.DeserializationConfig;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.jsontype.NamedType;
import com.fasterxml.jackson.databind.jsontype.TypeDeserializer;
import com.fasterxml.jackson.databind.jsontype.impl.StdTypeResolverBuilder;
public class CustomTypeResolver extends StdTypeResolverBuilder {
@Override
public TypeDeserializer buildTypeDeserializer(final DeserializationConfig config, final JavaType baseType, final Collection<NamedType> subtypes) {
return new CustomTypeDeserializer(baseType, null,
_typeProperty, _typeIdVisible, _defaultImpl);
}
}
现在我们有一个 CustomTypeResolver
用于子类型识别,但是当 Jackon 找到ParentClass"时,它如何知道查看这个类?
我们可以通过两种方式做到这一点:
Now we have a CustomTypeResolver
for sub type identification, but how Jackon will know to look this class when it found 'ParentClass'?
We can do it by two ways:
使用自定义配置扩展
JackonAnnotationInterceptor
并在创建ObjectMapper
时对其进行配置.
Extending
JackonAnnotationInterceptor
with custom configuration and configure it while creatingObjectMapper
.
使用 @JsonTypeResolver
注释.这是推荐的方法,因为我们不需要配置任何东西.
Using @JsonTypeResolver
annotation. This is the recommended approach as we don’t need to configure any thing.
在包含类型解析器后,我们的新 ParentClass
类将是:
After including type resolver our new ParentClass
class will be:
package com.dilipkumarg.tutorials.dynamicsubtype;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.databind.annotation.JsonTypeResolver;
@JsonTypeInfo(use = JsonTypeInfo.Id.NONE)
@JsonTypeResolver(CustomTypeResolver.class)
public class ParentClass {
private Header header;
private String nextField;
...
}
请参阅此处
这篇关于具有嵌套在对象中的类型属性的杰克逊多态反序列化的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持编程学习网!
本文标题为:具有嵌套在对象中的类型属性的杰克逊多态反序列化
基础教程推荐
- 由于对所需库 rt.jar 的限制,对类的访问限制? 2022-01-01
- 如何使用 Stream 在集合中拆分奇数和偶数以及两者的总和 2022-01-01
- 如何在不安装整个 WTP 包的情况下将 Tomcat 8 添加到 Eclipse Kepler 2022-01-01
- 首次使用 Hadoop,MapReduce Job 不运行 Reduce Phase 2022-01-01
- 在螺旋中写一个字符串 2022-01-01
- Spring Boot Freemarker从2.2.0升级失败 2022-01-01
- 如何对 HashSet 进行排序? 2022-01-01
- 如何使用 Eclipse 检查调试符号状态? 2022-01-01
- 如何强制对超级方法进行多态调用? 2022-01-01
- Java 中保存最后 N 个元素的大小受限队列 2022-01-01