【问题标题】:JSON deserializer with generic type using TypeReference使用 TypeReference 的具有泛型类型的 JSON 反序列化器
【发布时间】:2019-05-13 20:43:22
【问题描述】:

我正在尝试概括这种方法:

public EventStream<Greeting> deserialize(String value){
    ObjectMapper mapper = new ObjectMapper();

    EventStream<Greeting> data = null;
    try {
        data = new ObjectMapper().readValue(value, new TypeReference<EventStream<Greeting>>() {});
    } catch (IOException e) {
        e.printStackTrace();
    }
    return data;
}

EventStream 在哪里:

public class EventStream<T> {

    private EventHeaders headers;

    @JsonDeserialize
    private T payload;
}

我想要的是在反序列化方法中用泛型替换特定的 Object Greeting。

我试过这个:

public <T> EventStream<T> deserialize(String value){
    ObjectMapper mapper = new ObjectMapper();

    EventStream<T> data = null;
    try {
        data = new ObjectMapper().readValue(value, new TypeReference<EventStream<T>>() {});
    } catch (IOException e) {
        e.printStackTrace();
    }
    return data;
}

但 EventStream 结果中的负载被反序列化为 LinkedHashMap。似乎 TypeReference 忽略了泛型类型。 任何想法? 谢谢

【问题讨论】:

  • 如果可以反映泛型类型,TypeReference 一开始就不会存在。

标签: java serialization jackson deserialization json-deserialization


【解决方案1】:

您在这里遇到的一个常见问题是由称为类型擦除的东西引起的,即 java 实现泛型的方式。

类型擦除可以解释为强制类型的过程 仅在编译时约束并丢弃元素类型 运行时的信息。 [1]

所以当你尝试反序列化你的对象时,类型 T 是未知的,它只是被视为Object,反序列化结果将默认为Map(准确地说是LinkedHashMap)。

您可以通过将 targetClass 作为附加参数传递给函数调用来使您的方法具有通用性,如下所示:

public <T> EventStream<T> deserialize(String value, Class<T> targetClass)

然后你使用你的映射器的 TypeFactory 来创建这个 targetClass 的类型

JavaType type = mapper.getTypeFactory().constructParametricType(EventStream.class, targetClass);

您可以将其传递给 readValue 方法:

data = mapper.readValue(value, type);

完整代码:

public <T> EventStream<T> deserialize(String value, Class<T> targetClass){
    ObjectMapper mapper = new ObjectMapper();
    JavaType type = mapper.getTypeFactory()
        .constructParametricType(EventStream.class, targetClass);
    try {
        return mapper.readValue(value, type);
    } catch (IOException e) {
        e.printStackTrace();
    }
    return null;
}

[1]https://www.baeldung.com/java-type-erasure

【讨论】:

  • 我明白你关于类型擦除的观点,但你的解决方案应该是什么 targetClass 输入参数?我的意思是,如果我这样称呼它:EventStream&lt;Greeting&gt; data = deserialize(value, Greeting.class); 它仍然不起作用。我想我需要传递有关 EventStream 和泛型类型的信息。所以,我用这个改变了你的解决方案:JavaType type = mapper.getTypeFactory().constructParametricType(EventStream.class, targetClass); 它似乎有效。你觉得这里有什么缺点吗?
  • 是的,你需要传递 T 的类,所以在你的情况下就是Greeting.class。你是对的,你显然需要ParametricType 而不仅仅是Type - 编辑了我的答案。
  • 我认为这是一个可行的解决方案。我在这里看到的唯一缺点是你需要通过你的课程两次,首先作为通用参数T,然后作为实际课程targetClass,这感觉有点尴尬IMO。但是,我想不出更好的解决方案来实现您想要做的事情。
猜你喜欢
  • 2020-11-19
  • 1970-01-01
  • 1970-01-01
  • 2015-05-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-08-14
相关资源
最近更新 更多