面试官问:Java 中如何处理含有泛型的 JSON 反序列化问题?

public static void main(String[] args) {

List first = new ArrayList();

first.add(1);

first.add(“2”);

first.add(‘3’);

List third = first;

for (String each : third) { // 类型转换异常

System.out.println(each);

}

}

类型转换异常?

我们使用IDEA的jclasslib反编译插件,得到main函数的代码如下:

0 new #2 <java/util/ArrayList>

3 dup

4 invokespecial #3 <java/util/ArrayList.>

7 astore_1

8 aload_1

9 iconst_1

10 invokestatic #4 <java/lang/Integer.valueOf>

13 invokeinterface #5 <java/util/List.add> count 2

18 pop

19 aload_1

20 ldc #6 <2>

22 invokeinterface #5 <java/util/List.add> count 2

27 pop

28 aload_1

29 bipush 51

31 invokestatic #7 <java/lang/Character.valueOf>

34 invokeinterface #5 <java/util/List.add> count 2

39 pop

40 aload_1

41 astore_2

42 aload_2

43 invokeinterface #8 <java/util/List.iterator> count 1

48 astore_3

49 aload_3

50 invokeinterface #9 <java/util/Iterator.hasNext> count 1

55 ifeq 79 (+24)

58 aload_3

59 invokeinterface #10 <java/util/Iterator.next> count 1

64 checkcast #11 <java/lang/String>

67 astore_4

69 getstatic #12 <java/lang/System.out>

72 aload_4

73 invokevirtual #13 <java/io/PrintStream.println>

76 goto 49 (-27)

79 return

从42到76行对应foreach循环的逻辑,可以修剪列表的迭代器进行遍历,取出每个元素后强转为String类型,存储到局部变量表索引为4的位置,然后进行打印。

如果对反编译不熟悉可以去目标目录,双击编译后的类文件,使用IDEA自带的插件进行反编译:

//

// Source code recreated from a .class file by IntelliJ IDEA

// (powered by Fernflower decompiler)

//

package com.chujianyun.common.json;

import java.util.ArrayList;

import java.util.Iterator;

import java.util.List;

public class JsonGenericDemo {

public JsonGenericDemo() {

}

public static void main(String[] args) {

List first = new ArrayList();

first.add(1);

first.add(“2”);

first.add(‘3’);

List third = first;

Iterator var3 = first.iterator();

while(var3.hasNext()) {

String each = (String)var3.next();

System.out.println(each);

}

}

}

印证了上述说法,可见在String each =(String)var3.next(); 这里出现了类型转换异常。

三、解决之道


3.1猜想验证

我们猜测是不是可以通过某种途径将泛型作为参数传递给fastjson,让fastjson某个返回值是带泛型的,从而解决这个矛盾呢?

显然我们要去原始码中寻找,在JSONObject类中找到了以下的方法:

/**

 

* String jsonStr = “[{“id”:1001,“name”:“Jobs”}]”;

* List models = JSON.parseObject(jsonStr, new TypeReference<List>() {});

* @param text json string

* @param type type refernce

* @param features

* @return

*/

@SuppressWarnings(“unchecked”)

public static  T parseObject(String text, TypeReference type, Feature… features) {

return (T) parseObject(text, type.type, ParserConfig.global, DEFAULT_PARSER_FEATURE, features);

}

该函数的注释上还贴心地标注了相关用法,因此我们改造下:

public static void main(String[] args) {

String jsonString = “[“a”,“b”]”;

List list = JSONObject.parseObject(jsonString, new TypeReference<List>() {

});

System.out.println(list);

}

警告解除了。

所以大功告成?

难道上述做法可能为了消除一个警告,满足强迫症们的心愿而已吗???

且慢,我们看下面的例子:

import lombok.Data;

@Data

public class User {

private Long id;

private String name;

}

mport com.alibaba.fastjson.JSON;

import com.alibaba.fastjson.JSONObject;

import java.util.ArrayList;

import java.util.List;

public class JsonGenericDemo {

public static void main(String[] args) {

// 构造数据

User user = new User();

user.setId(0L);

user.setName(“tom”);

List users = new ArrayList<>();

users.add(user);

// 转为JSON字符串

String jsonString = JSON.toJSONString(users);

// 反序列化

List usersGet = JSONObject.parseObject(jsonString, List.class);

for (User each : usersGet) {

System.out.println(each);

}

}

}

大家执行上述示例会出现类型转换异常!

线程“主”中的异常java.lang.ClassCastException:com.alibaba.fastjson.JSONObject无法转换为com.chujianyun.common.json.com上的用户。com.chujianyun.common.json.JsonGenericDemo.main(JsonGenericDemo.java:26 )

有了第二部分的分析,大家可能就可以比较容易地想到

JSONObject.parseObject(jsonString, List.class) 构造出来的List存放的是JSONObject元素,foreach循环嵌套使用转换器遍历每个元素并强转为User类型是报类型转换异常。

那么为啥fastjson不能帮我们转换为List<User>类型呢?

有人说“由于泛型取向,没有泛型信息,所以无法逆向构造回初始类型”。

看下其实JSONObject.parseObject(jsonString, List.class);第一个参数的英文字符串,第二个参数是List.class。压根就没有提供泛型信息给FASTJSON。

作为这个工具函数本身,怎么猜得到要列出里面真正该存放啥类型呢?

因此如果能够通过某种途径,告诉它泛型的类型,就可以帮助您反序列化成真正的类型。

使用JSONObject.parseObject(jsonString, new TypeReference<List<User>>() { });即可。

因此我们使用TypeReference并同时为了消除警告,或者为了了解fastjson泛型的具体类型,正确反序列化泛型的类型。

那么突破原理是啥呢?我们看下com.alibaba.fastjson.TypeReference#TypeReference()

/**

* Constructs a new type literal. Derives represented class from type

* parameter.

Clients create an empty anonymous subclass. Doing so embeds the type

* parameter in the anonymous class’s type hierarchy so we can reconstitute it

* at runtime despite erasure.

*/

protected TypeReference(){

// 获取父类的 Type

Type superClass = getClass().getGenericSuperclass();

// 如果父类是参数化类型,会返回 java.lang.reflect.ParameterizedType

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注Java获取)

img

总结

对于面试还是要好好准备的,尤其是有些问题还是很容易挖坑的,例如你为什么离开现在的公司(你当然不应该抱怨现在的公司有哪些不好的地方,更多的应该表明自己想要寻找更好的发展机会,自己的一些现实因素,比如对于我而言是现在应聘的公司离自己的家更近,又或者是自己工作到达了迷茫期,想跳出迷茫期等等)

image

Java面试精选题、架构实战文档

整理不易,觉得有帮助的朋友可以帮忙点赞分享支持一下小编~

你的支持,我的动力;祝各位前程似锦,offer不断!
《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!
e5c14a7895254671a72faed303032d36.jpg" alt=“img” style=“zoom: 33%;” />

总结

对于面试还是要好好准备的,尤其是有些问题还是很容易挖坑的,例如你为什么离开现在的公司(你当然不应该抱怨现在的公司有哪些不好的地方,更多的应该表明自己想要寻找更好的发展机会,自己的一些现实因素,比如对于我而言是现在应聘的公司离自己的家更近,又或者是自己工作到达了迷茫期,想跳出迷茫期等等)

[外链图片转存中…(img-xvNA5yO6-1713015698351)]

Java面试精选题、架构实战文档

整理不易,觉得有帮助的朋友可以帮忙点赞分享支持一下小编~

你的支持,我的动力;祝各位前程似锦,offer不断!
《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!

  • 18
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Java使用fastjson进行泛型类的反序列化,你可以按照以下步骤进行操作: 首先,确保你已经引入了fastjson的依赖,可以通过Maven等构建工具添加以下依赖项: ```xml <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.79</version> </dependency> ``` 接下来,假设你有一个泛型类`Result<T>`,如下所示: ```java import com.alibaba.fastjson.JSON; public class Result<T> { private int code; private String message; private T data; public Result(int code, String message, T data) { this.code = code; this.message = message; this.data = data; } public int getCode() { return code; } public String getMessage() { return message; } public T getData() { return data; } public static <T> Result<T> fromJson(String json, Class<T> clazz) { return JSON.parseObject(json, new TypeReference<Result<T>>(clazz) {}.getType()); } } ``` 在上述代码,`fromJSON`方法使用fastjson的`parseObject`方法将JSON字符串反序列化为`Result<T>`对象。 然后,你可以使用以下代码将JSON字符串反序列化为具体的泛型类对象: ```java import com.alibaba.fastjson.JSON; public class Main { public static void main(String[] args) { String json = "{\"code\":200,\"message\":\"Success\",\"data\":{\"name\":\"John\",\"age\":25}}"; Result<User> result = Result.fromJson(json, User.class); System.out.println("Code: " + result.getCode()); System.out.println("Message: " + result.getMessage()); User user = result.getData(); System.out.println("Name: " + user.getName()); System.out.println("Age: " + user.getAge()); } } ``` 在上述代码,我们将一个包含`User`对象的JSON字符串反序列化为`Result<User>`对象,并输出其的字段值。 请注意,为了正确反序列化泛型类,我们需要通过传递`Class<T>`参数给`fromJSON`方法来指定具体的泛型类型。 以上就是使用fastjson进行泛型反序列化的基本示例。当然,根据实际需求和数据结构的复杂程度,你可能需要进行更多的定制和处理
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值