Java 里 JSONArray 转成 List 的几种写法

接口返回一个 JSON 数组,代码里需要 List<T>,中间这步转换几乎每个项目都会写。麻烦在于项目里可能同时引了 FastJSON、Gson、Jackson 三个库,写法各不一样,抄错一行就 ClassCastException。

一、FastJSON:一行搞定

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;

String json = "[{\"id\":1,\"name\":\"张三\"},{\"id\":2,\"name\":\"李四\"}]";

// 直接解析成 List
List<User> users = JSON.parseArray(json, User.class);

// 已经有 JSONArray 对象
JSONArray arr = JSON.parseArray(json);
List<User> users2 = arr.toJavaList(User.class);

toJavaList 内部会做类型转换,比 for 循环强转安全。要注意 fastjson 版本,1.2.24 之前有反序列化漏洞,建议用 fastjson2 或者最新 1.2.x。

二、Gson:需要 TypeToken

import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;

String json = "[{\"id\":1,\"name\":\"张三\"}]";
Type type = new TypeToken<List<User>>(){}.getType();
List<User> users = new Gson().fromJson(json, type);

TypeToken 的作用是保留泛型信息。Java 泛型在运行时会被擦除,直接写 List<User>.class 拿不到泛型,所以要用匿名子类的方式把类型"刻"在字节码里。这一步不能省,写成 new Gson().fromJson(json, List.class) 会得到 List<LinkedTreeMap>,取值时必然报错。

三、Jackson:TypeReference 同理

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

ObjectMapper mapper = new ObjectMapper();
List<User> users = mapper.readValue(json, new TypeReference<List<User>>(){});

Jackson 对字段命名有要求,User 类里如果字段是驼峰而 JSON 是下划线,需要加 @JsonProperty 或者配置命名策略。Gson 默认按字段名精确匹配,两边规则不一样,切换库时容易出问题。

四、org.json:最原始的方式

import org.json.JSONArray;

JSONArray arr = new JSONArray(json);
List<User> users = new ArrayList<>();
for (int i = 0; i < arr.length(); i++) {
    JSONObject o = arr.getJSONObject(i);
    User u = new User();
    u.setId(o.getInt("id"));
    u.setName(o.getString("name"));
    users.add(u);
}

org.json 是 Android 内置的库,不用额外依赖,但只能手动映射,字段多了很啰嗦。适合字段少又不想引入新依赖的场景。

五、Object 数组强制转换的老写法

List<Object> list = JSON.parseObject(json, List.class);
List<User> users = list.stream()
    .map(o -> JSON.parseObject(JSON.toJSONString(o), User.class))
    .collect(Collectors.toList());

先转 Object 再转字符串再反序列化,性能差,但兼容性好,库版本混乱时能救急。不推荐长期用。

六、踩过的坑

  • 数字类型:JSON 里的 1 反序列化到 Integer 字段没问题,字段是 Long 时 FastJSON 可能报错,显式声明泛型可以避免。
  • 日期格式:默认按时间戳解析,传的是 "2026-01-01 12:00:00" 会报错,要配 DateFormat 或者用 @JSONField(format=...)。
  • 空数组:"[]" 转出来是空 List,不是 null;"null" 转出来是 null。前端判空时两种都要处理。
  • 字段多余:JSON 里有但实体类没有的字段,Jackson 默认报 UnrecognizedPropertyException,加 @JsonIgnoreProperties(ignoreUnknown = true) 解决。
  • 嵌套泛型:List<Map<String, List<User>>> 这种,TypeToken 要完整写出来,不能只写外层。

七、怎么选

新项目统一用 Jackson 或 fastjson2,别三个库同时引。已有项目里已经用哪个就跟着用哪个,混用会导致同名字段解析结果不一致,排查起来特别费时间。