如何反序列化泛型 List<T>与杰克逊? [英] How to deserialize generic List<T> with Jackson?

查看:20
本文介绍了如何反序列化泛型 List<T>与杰克逊?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

多年来我一直在使用 Jackson 来序列化/反序列化对象,并且总是发现使用 TypeReference 反序列化 List 等不必要地复杂.我创建了一个简单的辅助函数:

public static TypeReference>列表() {return new TypeReference>(){}}

预期用途:

Listfoos = objectMapper.readValue(json, list());

它有效!的种类.通过调试器检查时,不是Foo 的列表,而是LinkedHashMap 的列表.我知道 ObjectMapper 反序列化为 LinkedHashMap 类型 Object,我在这里阅读了解释:

Jackson 和泛型类型参考

但是,为什么它可以将 List 分配给 List?至少那不应该是某种 ClassCastException 吗?

另外,有没有办法用 Java 的类型系统来做到这一点?

注意:下面的方法声明有同样的问题,这是有道理的,因为确定 T 不需要额外的参数:

public static TypeReference>listOf(Class 忽略){return new TypeReference>(){}}

解决方案

之所以这样工作,是因为Java 中的类型擦除.在开始阅读本答案的下一部分之前,请先阅读一下:

您现在可能知道,在阅读以上文章后,您编译后的方法如下所示:

static 类型引用<列表>listOf(Class 忽略){return new TypeReference(){};}

Jackson 将尝试为它找出最合适的类型,即 java.util.LinkedHashMap 用于 JSON 对象.要创建 irrefutable type 你需要使用 com.fasterxml.jackson.databind.type.TypeFactory 类.见下例:

import com.fasterxml.jackson.core.type.TypeReference;导入 com.fasterxml.jackson.databind.JavaType;导入 com.fasterxml.jackson.databind.ObjectMapper;导入 com.fasterxml.jackson.databind.type.TypeFactory;导入 java.io.File;导入 java.util.List;公共类 JsonTypeApp {public static void main(String[] args) 抛出异常 {File jsonFile = new File("./resource/test.json").getAbsoluteFile();ObjectMapper mapper = new ObjectMapper();System.out.println("尝试使用'TypeFactory'");列表ids = mapper.readValue(jsonFile, CollectionsTypeFactory.listOf(Id.class));System.out.println(ids);id id1 = ids.get(0);System.out.println(id1);System.out.println("尝试使用'TypeReference>'");列表地图 = mapper.readValue(jsonFile, CollectionsTypeFactory.erasedListOf(Id.class));System.out.println(地图);id maps1 = maps.get(0);System.out.println(maps1);}}类集合类型工厂{静态 JavaType listOf(Class clazz) {返回 TypeFactory.defaultInstance().constructCollectionType(List.class, clazz);}静态 <T>类型引用<列表>erasedListOf(Class 被忽略) {return new TypeReference(){};}}班级号 {私有整数 ID;//getter、setter、toString}

以上示例,对于以下 JSON 负载:

<预><代码>[{身份证":1},{身份证":22},{身份证":333}]

印刷品:

尝试使用TypeFactory"[{1}、{22}、{333}]{1}尝试使用 'TypeReference>'[{id=1}, {id=22}, {id=333}]线程main"中的异常 java.lang.ClassCastException:java.util.LinkedHashMap 无法转换为 com.example.Id在 com.example.JsonTypeApp.main(JsonTypeApp.java:27)

另见:

I've been using Jackson to serialize/deserialize objects for years and have always found it needlessly complicated to use TypeReference<T> to deserialize List etc. I created a simple helper function:

public static <T> TypeReference<List<T>> list() {
    return new TypeReference<List<T>>(){}
}

With intended use:

List<Foo> foos = objectMapper.readValue(json, list());

And it works! Kind of. When inspecting through the debugger, rather than a list of Foo, it is rather a list of LinkedHashMap. I understand that ObjectMapper deserializes into LinkedHashMap for type Object and I read the explanation for that here:

Jackson and generic type reference

However, why is it able to assign List<LinkedHasMap> to a List<Foo>? At the very least shouldn't that be some sort of ClassCastException?

Also, is there anyway to do this with Java's type system?

NOTE: the following method declaration has the same issue, which makes sense because the additional argument is not needed for T to be determined:

public static <T> TypeReference<List<T>> listOf(Class<T> ignored) {
    return new TypeReference<List<T>>(){}
}

解决方案

It works like this because of type erasure in Java. Please, read about it before you start reading next part of this answer:

As you probably know right now, after reading above articles, your method after compilation looks like this:

static <T> TypeReference<List> listOf(Class<T> ignored) {
    return new TypeReference<List>(){};
}

Jackson will try to find out the most appropriate type for it which will be java.util.LinkedHashMap for a JSON Object. To create irrefutable type you need to use com.fasterxml.jackson.databind.type.TypeFactory class. See below example:

import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.type.TypeFactory;

import java.io.File;
import java.util.List;

public class JsonTypeApp {

    public static void main(String[] args) throws Exception {
        File jsonFile = new File("./resource/test.json").getAbsoluteFile();

        ObjectMapper mapper = new ObjectMapper();

        System.out.println("Try with 'TypeFactory'");
        List<Id> ids = mapper.readValue(jsonFile, CollectionsTypeFactory.listOf(Id.class));
        System.out.println(ids);
        Id id1 = ids.get(0);
        System.out.println(id1);

        System.out.println("Try with 'TypeReference<List<T>>'");
        List<Id> maps = mapper.readValue(jsonFile, CollectionsTypeFactory.erasedListOf(Id.class));
        System.out.println(maps);
        Id maps1 = maps.get(0);
        System.out.println(maps1);
    }
}

class CollectionsTypeFactory {
    static JavaType listOf(Class clazz) {
        return TypeFactory.defaultInstance().constructCollectionType(List.class, clazz);
    }

    static <T> TypeReference<List> erasedListOf(Class<T> ignored) {
        return new TypeReference<List>(){};
    }
}

class Id {
    private int id;

    // getters, setters, toString
}

Above example, for below JSON payload:

[
  {
    "id": 1
  },
  {
    "id": 22
  },
  {
    "id": 333
  }
]

prints:

Try with 'TypeFactory'
[{1}, {22}, {333}]
{1}
Try with 'TypeReference<List<T>>'
[{id=1}, {id=22}, {id=333}]
Exception in thread "main" java.lang.ClassCastException: java.util.LinkedHashMap cannot be cast to com.example.Id
    at com.example.JsonTypeApp.main(JsonTypeApp.java:27)

See also:

这篇关于如何反序列化泛型 List&lt;T&gt;与杰克逊?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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