Jackson TypeReference在扩展时是否有效?

时间:2013-10-09 22:01:31

标签: java json jackson

以下代码段是不言自明的。您可以看到类型信息未被删除,但mapper不会获取类型信息。我猜是杰克逊不允许这样,对吧?如果我直接传递TypeReference,它会被正确反序列化。

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

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

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

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版。

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

1 个答案:

答案 0 :(得分:7)

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

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状态

  

返回表示实体的直接超类的Type   (类,接口,基本类型或void)由此类表示。

     

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

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

Object

创建的实例的直接超类是参数化类型new TypeReference<String>(){} ,根据javadoc,我们应该得到一个TypeReference实例,准确反映源代码中使用的实际类型参数< / EM>:

Type

然后您可以使用TypeReference<String> 获取参数化类型,并返回getActualTypeArguments()[0])

让我们举一个例子,使用匿名类和使用子类

进行可视化
String

运行

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

符合javadoc规则。 com.fasterxml.jackson.core.type.TypeReference<Test.AgentResponse<T>> Test.AgentResponse<T> 是源代码中的实际参数化类型。现在,如果相反,我们有

Test.AgentResponse<T>

我们得到结果

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

这也符合法案。内部类现在直接从Test.Subclass<java.lang.String> class java.lang.String 扩展,它在源代码中使用参数Subclass进行参数化。

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


请注意

AgentResponse

将编译并运行,但类型reader = new StringReader("{\"element\":{\"map-element\":[{\"name\":\"soto\", \"value\": 123}]}}"); obj = mapper.readValue(reader, new AgentReq<Map<String, Set<Whatever>>>()); 将丢失。 Jackson将使用默认类型来序列化JSON。 AgentReq<Map<String, Set<Whatever>>>将被反序列化为element,而AgentResponse将被反序列化为map-element,而JSON数组将被反序列化为Map