使用fastjson将字符串转换为带泛型的Java对象

正常情况下,我们使用JSONObject.parseObject(String body, Class clazz)方法就可以将字符串转换为一个Java对象,但是如果我们要转的Java对象是一个带泛型类,该咋整呢?

这个时候我们可以使用fastjson里面自带的TypeReference类进行转换,具体语句如下:

1
2
String messageBody = "";
MessageBody<Item> messageItemBody = JSON.parseObject(messageBody, new TypeReference<MessageBody<Item>>(){})

这样就可以了,是不是挺神奇的,下面我们来看下具体的实现逻辑,为什么我们使用TypeReference就可以转成泛型类的对象。


  1. 第一步当然是看下JSON#parseObject这个方法的内部实现,代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 入口:
public static final <T> T parseObject(String text, TypeReference<T> type, Feature... features) {
return (T) parseObject(text, type.getType(), ParserConfig.getGlobalInstance(), DEFAULT_PARSER_FEATURE, features);
}


// 底口
public static final <T> T parseObject(String input, Type clazz, ParserConfig config, ParseProcess processor,
int featureValues, Feature... features) {
// 省略转换和校验代码...

T value = (T) parser.parseObject(clazz);

handleResovleTask(parser, value);

parser.close();

return (T) value;
}

我们在最底层的方法中发现了一句话T value = (T) parser.parseObject(clazz);这里传进来的clazz对象是java.lang.reflect.Type类型的,大家可以看一下这个类,很神奇,这里重点不是它,不做说明了。

在入口方法处,我们找到了type.getType()语句,那么可以断定,神奇的根本原因在TypeReference类中,既然如此,那就进到这个类中看一下。

  1. 第二步就是查看com.alibaba.fastjson.TypeReference类的实现(版本1.2.16)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/**
* Constructs a new type literal. Derives represented class from type
* parameter.
*
* <p>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 superClass = getClass().getGenericSuperclass();

Type type = ((ParameterizedType) superClass).getActualTypeArguments()[0];

Type cachedType = classTypeCache.get(type);
if (cachedType == null) {
classTypeCache.putIfAbsent(type, type);
cachedType = classTypeCache.get(type);
}

this.type = cachedType;
}

代码中最核心的一句是Type type = ((ParameterizedType) superClass).getActualTypeArguments()[0];,这句话的意思是获得超类的泛型参数的实际类型,也就是获得泛型T的实际类型type。看到这里有点懵,到底我们获取到的type是什么?简单写一个case看下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;

import com.alibaba.fastjson.TypeReference;

public class TypeReferenceTest {

public static void main(String[] args) {
// ①
TypeReference reference = new TypeReference<ArrayList<String>>() {
};
Type superClass = reference.getClass().getGenericSuperclass();
Type type = ((ParameterizedType) superClass).getActualTypeArguments()[0];

System.out.println(type);
// ②
System.out.println((Type) (ArrayList.class));
}

}

输出结果为:

1
2
java.util.ArrayList<java.lang.String>
class java.util.ArrayList

看得出,打印出的结果是完全不同的,也就是说,我们在反序列化的时候,代码①会保留泛型约束,代码②会抛弃泛型约束。

  1. 再看一个案例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;

public class TypeReferenceTest {

public static void main(String[] args) {
String json = "[\"abc\",\"def\",\"ghi\"]";
// ①
ArrayList<System> list1 = JSON.parseObject(json, ArrayList.class);
System.out.println(list1.get(0));
// ②
ArrayList<System> list2 = JSON.parseObject(json, new TypeReference<ArrayList<String>>() {
});
}
}

代码①中,不论ArrayList中放置的泛型是什么类型,编译和运行都不会报错,且能正确的序列化,而代码②会在编译器进行校验,也就是说使用TypeReference会校验泛型是否匹配