当前位置: 首页 > 面试题库 >

如何在Jackson中为通用类型创建自定义解串器?

贺元明
2023-03-14
问题内容

想象以下情况:

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

class Bar {
    Foo<Something> foo;
}

我想为Foo写一个定制的Jackson解串器。为了做到这一点(例如,为了反序列化Bar类,它具有Foo<Something>属性),我需要知道的具体类型Foo<T>,在使用Bar,在反序列化时间(比如我需要知道TSomething在particluar情况下)。

一个人怎么写这样的反序列化器?因为杰克逊使用类型化的集合和地图来做到这一点,所以应该有可能做到这一点。

说明:

似乎有两个部分可以解决问题:

1)获取foo内部声明的属性类型Bar并使用其反序列化Foo<Somehting>

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

一个如何完成1和2?


问题答案:

您可以JsonDeserializer为自己的通用类型实现自定义,该自定义类型也可以实现ContextualDeserializer

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

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

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

{
    "name": "Alice",
    "age": 37
}

变成如下所示的类的实例:

public static class Person {
    public Wrapper<String> name;
    public Wrapper<Integer> age;
}

实现ContextualDeserializer使我们能够Person基于字段的通用类型参数为类中的每个字段创建特定的反序列化器。这使我们可以将名称反序列化为字符串,将年龄反序列化为整数。

完整的解串器如下所示:

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这里,因为这将由杰克逊首先调用。我们从中读取字段的类型BeanProperty(例如Wrapper<String>),然后提取第一个通用类型参数(例如String)。然后,我们创建一个新的反序列化器并将内部类型存储为valueType

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

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

SimpleModule module = new SimpleModule()
        .addDeserializer(Wrapper.class, new WrapperDeserializer());

ObjectMapper objectMapper = new ObjectMapper();
objectMapper.registerModule(module);

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

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

在Jackson文档中,有更多有关上下文反序列化器如何工作的详细信息。



 类似资料:
  • 想象一下以下场景: 我想为Foo编写一个自定义的Jackson反序列化程序。为了做到这一点(例如,为了反序列化具有Foo的类 如何编写这样的反序列化程序?这应该是可能的,因为Jackson是通过键入集合和地图来实现的。 澄清: 似乎有两个部分可以解决这个问题: 1)获取内声明的属性类型并使用它来反序列化

  • 我希望在Jackson中为Enum类的集合创建一个自定义反序列化器。因为自定义反序列化器的行为对于所有枚举都是相同的。我想为我的所有枚举类创建公共反序列化程序。 我尝试使一般自定义反序列化如下: 问题是我想在反序列化器中调用枚举静态方法,但无法这样做,因为我没有任何可用的类/枚举上下文信息。 你能帮我知道如何实现它吗?

  • 我已经创建了一个消费者来接收来自Kafka主题的消息,但是我正在手动解析消息,有没有一种方法可以使用自定义的反序列化器来自动解析这个主题消费者? 预期结果是: 如果出现解析错误,我可以设置回调来处理此错误吗?

  • 嗨下面这个问题 如何在Jackson中为泛型类型创建自定义反序列化器? 我想知道如何通过这个来解析 这就是我目前所拥有的 当我试图反序列化一点这个的时候,我得到了一个NPE

  • 问题内容: 我在Jackson的自定义解串器中遇到问题。我想访问默认的序列化器以填充要反序列化的对象。填充之后,我将执行一些自定义操作,但首先,我想使用默认的Jackson行为反序列化对象。 这是我目前拥有的代码。 我需要的是一种初始化默认反序列化器的方法,以便在启动特殊逻辑之前可以预先填充POJO。 从自定义反序列化器中调用反序列化时,无论我如何构造序列化器类,似乎都是从当前上下文中调用该方法。

  • 问题内容: 例如,我想创建一个类似于AngularJS实现“电子邮件”的自定义输入类型。 我要创建的是这样的输入类型: 关于如何做到这一点的任何想法?到目前为止,我仅能弄清楚如何实现自定义指令,其中“ path”是标记,属性或类的名称。 例如,我可以使它工作,但是它与其他表单字段 不一致 ,我真的希望它们看起来相同。 问题答案: 如果type属性设置为“ path”,则可以通过使用自定义逻辑创建输