引言:为什么每个Java开发者都应该掌握这个技巧
想象一下:你从服务器获取了一段JSON,其中包含数十个同类型对象。客户、商品、日志——无所谓。你拿出ObjectMapper,写下readValue(json, MyClass[].class)——一切正常。但一个月后,代码却因ClassCastException崩溃,因为有人把数组换成了List。熟悉吗?
Stack Overflow上关于“How to use Jackson to deserialise an array of objects”的问题已获得数万次浏览。这并非偶然:Java中的对象数组反序列化看似简单,直到你遇到泛型和类型擦除。2025年,Jackson仍是处理JSON的事实标准,但最佳实践已经演变。本文将不仅讲解基础方法,还会介绍能让你避免头疼的现代技术。
基础方法:数组还是列表?
最简单的方式是使用数组。Jackson完全理解MyClass[].class表示对象数组:
ObjectMapper mapper = new ObjectMapper();String json = "[{\\"id\\":1,\\"name\\":\\"Alice\\"},{\\"id\\":2,\\"name\\":\\"Bob\\"}]";MyClass[] array = mapper.readValue(json, MyClass[].class);能工作吗?能。但这种方法有缺点:
- 数组大小固定——不适合动态数据。
- 没有集合方法(
stream()、filter()、remove())。 - 如果后续需要
List,扩展起来更困难。
因此,90%的情况下你会希望得到List<MyClass>。这时有趣的部分开始了——Java的类型擦除。如果你写成:
List<MyClass> list = mapper.readValue(json, List.class); // warning: unchecked编译器会发出警告,而运行时你会得到List<LinkedHashMap>,而不是List<MyClass>。泛型被擦除,Jackson不知道要将元素映射成什么类型。
TypeReference:处理List的正确方式
解决方案是使用TypeReference。这是一个匿名内部类,通过反射保留泛型信息:
List<MyClass> list = mapper.readValue(json, new TypeReference<List<MyClass>>() {});注意{}——这会创建一个捕获类型的匿名类。Jackson通过TypeFactory理解需要将List反序列化为MyClass类型。这种方法在2025年仍是黄金标准。
什么时候TypeReference不方便?
- 当你需要动态构建类型时(例如从配置中获取)。
- 当需要多次反序列化同一类型时——每次都创建匿名类开销较大。
对于这些情况,有替代方案——CollectionType。
CollectionType:JavaType的强大之处
Jackson提供了TypeFactory来构建复杂类型:
JavaType type = mapper.getTypeFactory() .constructCollectionType(List.class, MyClass.class);List<MyClass> list = mapper.readValue(json, type);这种方法:
- 不创建匿名类(开销更小)。
- 允许动态构建类型:
constructParametricType(Map.class, String.class, MyClass.class)。 - 非常适合库和框架,其中类型在编译时未知。
2025年最佳实践:简单场景使用TypeReference,需要灵活性或性能时使用CollectionType。
Reader和InputStream:别忘了资源管理
博客
书籍推荐
推荐阅读