Java 如何在 Jackson 中为泛型类型创建自定义反序列化器?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/36159677/
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
How to create a custom deserializer in Hymanson for a generic type?
提问by Kre?imir Nesek
Imagine the following scenario:
想象以下场景:
class <T> Foo<T> {
....
}
class Bar {
Foo<Something> foo;
}
I want to write a custom Hymanson deserializer for Foo. In order to do that (for example, in order to deserialize Bar
class that has Foo<Something>
property), I need to know the concrete type of Foo<T>
, used in Bar
, at deserialization time (e.g. I need to know that T
is Something
in that particluar case).
我想为 Foo 编写一个自定义的 Hymanson 反序列化器。为了做到这一点(例如,为了反序列化Bar
具有Foo<Something>
属性的类),我需要知道在反序列化时Foo<T>
使用的的具体类型Bar
(例如,我需要知道那T
是Something
在那种特殊情况下)。
How does one write such a deserializer? It should be possible to do it, since Hymanson does it with typed collections and maps.
如何编写这样的解串器?应该可以做到这一点,因为 Hymanson 使用类型化集合和地图来做到这一点。
Clarifications:
说明:
It seems there are 2 parts to solution of the problem:
似乎有两个部分可以解决问题:
1) Obtain declared type of property foo
inside Bar
and use that to deserialize Foo<Somehting>
1)获取foo
内部声明的属性类型Bar
并使用它来反序列化Foo<Somehting>
2) Find out at deserialization time that we are deserializing property foo
inside class Bar
in order to successfully complete step 1)
2) 在反序列化时发现我们正在foo
类Bar
中反序列化属性以成功完成步骤 1)
How does one complete 1 and 2 ?
如何完成 1 和 2 ?
采纳答案by andersschuller
You can implement a custom JsonDeserializer
for your generic type which also implements ContextualDeserializer
.
您可以JsonDeserializer
为您的泛型类型实现自定义,它也实现ContextualDeserializer
.
For example, suppose we have the following simple wrapper type that contains a generic value:
例如,假设我们有以下包含泛型值的简单包装器类型:
public static class Wrapper<T> {
public T value;
}
We now want to deserialize JSON that looks like this:
我们现在想要反序列化如下所示的 JSON:
{
"name": "Alice",
"age": 37
}
into an instance of a class that looks like this:
进入一个看起来像这样的类的实例:
public static class Person {
public Wrapper<String> name;
public Wrapper<Integer> age;
}
Implementing ContextualDeserializer
allows us to create a specific deserializer for each field in the Person
class, based on the generic type parameters of the field. This allows us to deserialize the name as a string, and the age as an integer.
实现ContextualDeserializer
允许我们根据字段Person
的泛型类型参数为类中的每个字段创建特定的反序列化器。这允许我们将名称反序列化为字符串,将年龄反序列化为整数。
The complete deserializer looks like this:
完整的解串器如下所示:
public static class WrapperDeserializer extends JsonDeserializer<Wrapper<?>> implements ContextualDeserializer {
private JavaType valueType;
@Override
public JsonDeserializer<?> createContextual(DeserializationContext ctxt, BeanProperty property) throws JsonMappingException {
JavaType wrapperType = property.getType();
JavaType valueType = wrapperType.containedType(0);
WrapperDeserializer deserializer = new WrapperDeserializer();
deserializer.valueType = valueType;
return deserializer;
}
@Override
public Wrapper<?> deserialize(JsonParser parser, DeserializationContext ctxt) throws IOException {
Wrapper<?> wrapper = new Wrapper<>();
wrapper.value = ctxt.readValue(parser, valueType);
return wrapper;
}
}
It is best to look at createContextual
here first, as this will be called first by Hymanson. We read the type of the field out of the BeanProperty
(e.g. Wrapper<String>
) and then extract the first generic type parameter (e.g. String
). We then create a new deserializer and store the inner type as the valueType
.
最好先看createContextual
这里,因为Hyman逊会先调用这里。我们从BeanProperty
(eg Wrapper<String>
) 中读取字段的类型,然后提取第一个泛型类型参数 (eg String
)。然后我们创建一个新的反序列化器并将内部类型存储为valueType
.
Once deserialize
is called on this newly created deserializer, we can simply ask Hymanson to deserialize the value as the inner type rather than as the whole wrapper type, and return a new Wrapper
containing the deserialized value.
一旦deserialize
在这个新创建的反序列化器上被调用,我们可以简单地要求 Hymanson 将值反序列化为内部类型而不是整个包装类型,并返回一个Wrapper
包含反序列化值的新值。
In order to register this custom deserializer, we then need to create a module that contains it, and register that module:
为了注册这个自定义反序列化器,我们需要创建一个包含它的模块,并注册该模块:
SimpleModule module = new SimpleModule()
.addDeserializer(Wrapper.class, new WrapperDeserializer());
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.registerModule(module);
If we then try to deserialize the example JSON from above, we can see that it works as expected:
如果我们然后尝试反序列化上面的示例 JSON,我们可以看到它按预期工作:
Person person = objectMapper.readValue(json, Person.class);
System.out.println(person.name.value); // prints Alice
System.out.println(person.age.value); // prints 37
There are some more details about how contextual deserializers work in the Hymanson documentation.
Hymanson 文档中有更多关于上下文反序列化器如何工作的详细信息。
回答by Ahmed Mkaouar
If the target itself is a generic type then property will be null, for that you'll need to get the valueTtype from the DeserializationContext:
如果目标本身是泛型类型,则属性将为空,为此您需要从 DeserializationContext 获取 valueTtype:
@Override
public JsonDeserializer<?> createContextual(DeserializationContext ctxt, BeanProperty property) throws JsonMappingException {
if (property == null) { // context is generic
JMapToListParser parser = new JMapToListParser();
parser.valueType = ctxt.getContextualType().containedType(0);
return parser;
} else { // property is generic
JavaType wrapperType = property.getType();
JavaType valueType = wrapperType.containedType(0);
JMapToListParser parser = new JMapToListParser();
parser.valueType = valueType;
return parser;
}
}
回答by Pratap Singh
This is how you can access/resolve {targetClass} for a Custom Hymanson Deserializer. Of course you need to implement ContextualDeserializer interface for this.
这是您如何访问/解析自定义 Hymanson Deserializer 的 {targetClass}。当然,您需要为此实现 ContextualDeserializer 接口。
public class WPCustomEntityDeserializer extends JsonDeserializer<Object>
implements ContextualDeserializer {
private Class<?> targetClass;
@Override
public Object deserialize(JsonParser jp, DeserializationContext ctxt)
throws IOException, JsonProcessingException {
ObjectCodec oc = jp.getCodec();
JsonNode node = oc.readTree(jp);
//Your code here to customize deserialization
// You can access {target class} as targetClass (defined class field here)
//This should build some {deserializedClasObject}
return deserializedClasObject;
}
@Override
public JsonDeserializer<?> createContextual(DeserializationContext ctxt, BeanProperty property){
//Find here the targetClass to be deserialized
String targetClassName=ctxt.getContextualType().toCanonical();
try {
targetClass = Class.forName(targetClassName);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return this;
}
}