本文介绍了Jackson TypeReference 在扩展时是否有效?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

以下代码段是不言自明的.可以看到类型信息没有被擦除,但是mapper并没有得到类型信息.我的猜测是杰克逊不允许这样做,对吧?如果我直接传递 TypeReference,它会被正确反序列化.

Following snippet is self-explanatory enough. You can see that type information is not erased, but mapper doesn't get the type information. My guess is that jackson doesn't allow this, right ? If I pass TypeReference directly, it is deserialized properly.

public class AgentReq<T> extends TypeReference<AgentResponse<T>> {...}

mapper.readValue(reader, new AgentReq<Map<String, Set<Whatever>>>());

如果我这样做也不起作用:

It also doesn't work if I do this :

public class AgentReq<T> {

    public TypeReference<AgentResponse<T>> getTypeRef() {
        return new TypeReference<AgentResponse<T>>() {};
    }
}

mapper.readValue(reader, new AgentReq<Map<String, Set<Whatever>>>()).getTypeRef();

我使用的是 2.1.5 版.

I'm using version 2.1.5.

为了将来参考,在解决问题时不要低估 TypeReference 构造函数.在那里您可以直接看到它是否能够检索类型信息.顺便说一句,答案是否定的,你不能扩展 TypeReference 并期望它工作,你甚至不能覆盖它的 getType() 方法并向它提供从你的类解析的类型信息,因为你只能得到 getClass().getGenericSuperClass() ... 你不能做 getClass().getGenericClass()

For future reference, do not underestimate the TypeReference constructor when resolving problems. There you can see directly whether it was able to retrieve type information. Btw the answer is NO, you can't extend TypeReference and expect it to work, you can't even override its getType() method and supply it with type information resolved from your class, because all you can get is getClass().getGenericSuperClass() ... You can't do getClass().getGenericClass()

推荐答案

您需要了解 TypeReference 的工作原理.为此,我们进入源代码

You need to understand how a TypeReference works. For that we go into the source code

protected TypeReference()
{
    Type superClass = getClass().getGenericSuperclass();
    if (superClass instanceof Class<?>) { // sanity check, should never happen
        throw new IllegalArgumentException("Internal error: TypeReference constructed without actual type information");
    }
    ...
    _type = ((ParameterizedType) superClass).getActualTypeArguments()[0];
}

Class#getGenericSuperclass() javadoc 声明

The Class#getGenericSuperclass() javadoc states

返回代表实体直接超类的类型(类、接口、原始类型或void)由此类表示.

如果超类是参数化类型,返回的Type对象必须准确反映源中使用的实际类型参数代码.

If the superclass is a parameterized type, the Type object returned must accurately reflect the actual type parameters used in the source code.

换句话说,如果我们可以做 new TypeReference()(我们不能,它是抽象的),它将返回 Class 类的实例 >对象.但是,使用匿名类(从类型扩展)

In other words, if we could do new TypeReference() (we can't, it's abstract), it would return the Class instance for the class Object. However, with anonymous classes (which extend from the type)

new TypeReference<String>(){}

创建的实例的直接超类是参数化类型TypeReference,根据javadoc我们应该得到一个Type实例,准确反映实际的类型参数在源代码中使用:

the direct superclass of the instance created is the parameterized type TypeReference and according to the javadoc we should get a Type instance that accurately reflect the actual type parameters used in the source code:

TypeReference<String>

然后您可以使用 getActualTypeArguments()[0]) 从中获取参数化类型,返回 String.

from which you can then get the parameterized type with getActualTypeArguments()[0]), returning String.

让我们举一个例子来可视化使用匿名类和使用子类

Let's take an example to visualize using anonymous class and using a sub-class

public class Subclass<T> extends TypeReference<AgentResponse<T>>{
    public Subclass() {
        System.out.println(getClass().getGenericSuperclass());
        System.out.println(((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0]);
    }
}

运行

new Subclass<String>();

印刷品

com.fasterxml.jackson.core.type.TypeReference<Test.AgentResponse<T>>
Test.AgentResponse<T>

符合 javadoc 规则.Test.AgentResponse 是源代码中的实际参数化类型.现在,如果相反,我们有

which fits the javadoc rules. Test.AgentResponse<T> is the actual parameterized type in the source code. Now, if instead, we had

new Subclass<String>(){}; // anonymous inner class

我们得到结果

Test.Subclass<java.lang.String>
class java.lang.String

这也符合要求.内部类现在直接从 Subclass 扩展而来,后者在源代码中使用参数 String 进行参数化.

which also fits the bill. The inner class now extends directly from Subclass which is parameterized with the argument String in the source code.

您会注意到,使用 Subclass 匿名内部类,我们丢失了关于 AgentResponse 泛型类型的信息.这是不可避免的.

You will notice that, with the Subclass anonymous inner class, we've lost information about the AgentResponse generic type. This is unavoidable.

注意

reader = new StringReader("{"element":{"map-element":[{"name":"soto", "value": 123}]}}");
obj = mapper.readValue(reader, new AgentReq<Map<String, Set<Whatever>>>());

将编译并运行,但 AgentReq> 类型将丢失.Jackson 将使用默认类型来序列化 JSON.element 将反序列化为 AgentResponse,而 map-element 将反序列化为 Map 和 JSON数组作为 ArrayList.

will compile and run, but the type AgentReq<Map<String, Set<Whatever>>> will have been lost. Jackson will use default type to serializes the JSON. The element will be deserialized as an AgentResponse, while map-element will be deserialized as a Map and the JSON array as an ArrayList.

这篇关于Jackson TypeReference 在扩展时是否有效?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

10-27 22:10