如何在 Jackson 中为泛型类型创建自定义反序列化器? [英] How to create a custom deserializer in Jackson for a generic type?

查看:31
本文介绍了如何在 Jackson 中为泛型类型创建自定义反序列化器?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

想象以下场景:

class <T> Foo<T> {
    ....
}

class Bar {
    Foo<Something> foo;
}

我想为 Foo 编写一个自定义的 Jackson 反序列化器.为了做到这一点(例如,为了反序列化具有 Foo 属性的 Bar 类),我需要知道 Foo<;T>,在 Bar 中使用,在反序列化时(例如,我需要知道 TSomething 在那种特殊情况下).

I want to write a custom Jackson 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).

如何编写这样的解串器?应该可以做到这一点,因为 Jackson 使用类型化集合和地图来做到这一点.

How does one write such a deserializer? It should be possible to do it, since Jackson does it with typed collections and maps.

说明:

问题的解决似乎有两部分:

It seems there are 2 parts to solution of the problem:

1) 在 Bar 中获取声明类型的属性 foo 并使用它反序列化 Foo

1) Obtain declared type of property foo inside Bar and use that to deserialize Foo<Somehting>

2) 在反序列化时发现我们正在对 Bar 类中的属性 foo 进行反序列化,以成功完成步骤 1)

2) Find out at deserialization time that we are deserializing property foo inside class Bar in order to successfully complete step 1)

如何完成 1 和 2 ?

How does one complete 1 and 2 ?

推荐答案

您可以实现自定义 JsonDeserializer 用于您的泛型类型,它也实现了 ContextualDeserializer.

You can implement a custom JsonDeserializer for your generic type which also implements ContextualDeserializer.

例如,假设我们有以下包含泛型值的简单包装器类型:

For example, suppose we have the following simple wrapper type that contains a generic value:

public static class Wrapper<T> {
    public T value;
}

我们现在想要反序列化如下所示的 JSON:

We now want to deserialize JSON that looks like this:

{
    "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;
}

实现ContextualDeserializer 允许我们根据字段的泛型类型参数为Person 类中的每个字段创建一个特定的反序列化器.这允许我们将名称反序列化为字符串,将年龄反序列化为整数.

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.

完整的解串器如下所示:

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;
    }
}

最好先在此处查看 createContextual,因为 Jackson 将首先调用它.我们从 BeanProperty 中读取字段的类型(例如 Wrapper),然后提取第一个泛型类型参数(例如 String).然后我们创建一个新的解串器并将内部类型存储为 valueType.

It is best to look at createContextual here first, as this will be called first by Jackson. 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.

一旦在这个新创建的反序列化器上调用 deserialize,我们可以简单地要求 Jackson 将值反序列化为内部类型而不是整个包装类型,并返回一个新的 Wrapper 包含反序列化的值.

Once deserialize is called on this newly created deserializer, we can simply ask Jackson to deserialize the value as the inner type rather than as the whole wrapper type, and return a new Wrapper containing the deserialized value.

为了注册这个自定义反序列化器,我们需要创建一个包含它的模块,并注册该模块:

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);

如果我们然后尝试反序列化上面的示例 JSON,我们可以看到它按预期工作:

If we then try to deserialize the example JSON from above, we can see that it works as expected:

Person person = objectMapper.readValue(json, Person.class);
System.out.println(person.name.value);  // prints Alice
System.out.println(person.age.value);   // prints 37

Jackson 文档中有更多关于上下文解串器如何工作的详细信息.

There are some more details about how contextual deserializers work in the Jackson documentation.

这篇关于如何在 Jackson 中为泛型类型创建自定义反序列化器?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆