Fastjson2 常见问题
作为 Java 开发中常用的 JSON 解析库,Fastjson2 相比旧版性能更优、安全性更高。但新手在使用时仍会遇到一些常见问题。以下是Fastjson2使用过程中常见的一些问题。
以下是 Fastjson2 使用中最常遇到的 4 个问题和坑点,每个都配有错误示例、报错信息和解决方案。
一、常见 FAQ
Q1:反序列化时报错
报错信息示例:
com.alibaba.fastjson2.JSONException: can not cast to JSONObject
com.alibaba.fastjson2.JSONException: can not cast to int
原因:JSON 字段与 Java 类的字段名不匹配,或类型不兼容。
错误示例:
// JSON 数据:{"user_name":"张三","age":"abc"} // age 是字符串
// Java 类
public class User {
private String userName; // JSON 中是 user_name,不匹配
private int age; // JSON 中是字符串 "abc"
}
// 调用代码
User user = JSON.parseObject(json, User.class);
System.out.println(user.getUserName()); // null
System.out.println(user.getAge()); // 报错
Fastjson2 对字符串形式的数字(如 "18")会自动转成 int/long/BigDecimal,上面的例子中age只需要改为数字或数字字符串,对于userName有两个解决方案
解决方案:
public class User {
// 用 @JSONField 映射字段名
@JSONField(name = "user_name")
private String userName;
}
更简单的方案:开启智能匹配(自动处理驼峰/下划线、字符串/数字互转)
User user = JSON.parseObject(json, User.class,
JSONReader.Feature.SupportSmartMatch);
Q2:泛型类型解析
错误写法:
String json = "[{\"name\":\"张三\"},{\"name\":\"李四\"}]";
// 错误:直接传 List.class
List<User> list = JSON.parseObject(json, List.class);
User user = list.get(0); // 报错:ClassCastException: JSONObject cannot be cast to User
报错信息:
java.lang.ClassCastException: com.alibaba.fastjson2.JSONObject cannot be cast to com.example.User
正确写法:
import com.alibaba.fastjson2.TypeReference;
// 方案1:TypeReference(推荐)
List<User> userList = JSON.parseObject(json, new TypeReference<List<User>>() {});
User user = userList.get(0); // 直接是 User 类型,无需强转
// 方案2:parseArray(简单场景可用)
List<User> userList = JSON.parseArray(json, User.class);
注意:new TypeReference<List<User>>() {} 后面的 {} 绝对不能省,少了就是运行时擦除。
Q3:序列化时不想包含某个字段?
场景:密码字段不能输出到 JSON,使用@JSONField 注解。
解决方案:
public class User {
private String username;
@JSONField(serialize = false) // 序列化时忽略
private String password;
@JSONField(deserialize = false) // 反序列化时忽略
private String confirmPassword;
// getter/setter
}
验证:
User user = new User();
user.setUsername("admin");
user.setPassword("123456");
String json = JSON.toJSONString(user);
System.out.println(json); // 输出:{"username":"admin"},不包含 password
Q4:Fastjson2 和 Fastjson1 对比?
| 对比项 | Fastjson1 | Fastjson2 |
|---|---|---|
| 包名 | com.alibaba.fastjson | com.alibaba.fastjson2 |
| 安全漏洞 | AutoType 历史问题较多 | 默认关闭 AutoType,更安全 |
| 性能 | 快 | 更快(20%-30% 提升) |
| API 风格 | 较复杂 | 更精简统一 |
| 维护状态 | 仅修复严重 Bug | 活跃维护 |
迁移建议:新项目直接用 Fastjson2,旧项目逐步替换。
二、避坑指南(附报错信息)
坑2:循环引用导致栈溢出
报错信息:
Exception in thread "main" com.alibaba.fastjson2.JSONException: level too large : 2048
复现代码:
public class Department {
private String name;
private List<Employee> employees;
// getter/setter
}
public class Employee {
private String name;
private Department department; // 反向引用部门
// getter/setter
}
// 构建循环引用
Department dept = new Department();
dept.setName("技术部");
Employee emp = new Employee();
emp.setName("张三");
emp.setDepartment(dept); // 员工指向部门
dept.setEmployees(Arrays.asList(emp)); // 部门包含员工
// 序列化 → 栈溢出
String json = JSON.toJSONString(dept);
解决方案:
// 方案1:序列化时禁用循环引用检测
String json = JSON.toJSONString(dept,
JSONWriter.Feature.ReferenceDetection);//fastjson1是JSONWriter.Feature.DisableCircularReferenceDetect
// 方案2:在一边切断引用(推荐)
public class Employee {
private String name;
@JSONField(serialize = false) // 序列化时不输出 department
private Department department;
}
坑3:AutoType 安全问题
背景:Fastjson1 中 @type 参数允许指定类名,攻击者可利用它执行任意代码。
Fastjson2 的改进:默认关闭 AutoType,需要手动开启。
错误的安全配置(千万别这么做):
// 千万不要全局允许所有包
JSON.config(JSONReader.Feature.SupportAutoType); // 危险!
正确的安全配置:
// 如果必须使用 AutoType,只允许特定包名
JSON.config(JSONReader.Feature.SupportAutoType);
ParserConfig.getGlobalInstance().addAccept("com.example.model."); // 白名单
ParserConfig.getGlobalInstance().addAccept("com.example.dto.User");
最佳实践:避免使用 AutoType,改用 TypeReference。
坑4:日期格式不对
默认行为:Date 序列化后变成时间戳(毫秒数)。
String json = JSON.toJSONString(new Date());
System.out.println(json); // 输出:2026-05-08 10:50:49.078(不是标准的时间格式)
解决方案:
// 方案1:序列化时指定格式
String json = JSON.toJSONString(new Date(),"yyyy-MM-dd HH:mm:ss");
// 输出:"2026-05-08 10:53:38"
// 方案2:全局配置日期格式
JSON.configWriterDateFormat("yyyy-MM-dd HH:mm:ss");
// 方案3:字段级控制
public class Order {
@JSONField(format = "yyyy-MM-dd HH:mm:ss")
private Date createTime;
}
坑5:null 值在 JSON 中丢失
默认行为:null 值的字段不输出到 JSON。
User user = new User();
user.setName("张三"); // age 和 address 为 null
String json = JSON.toJSONString(user);
System.out.println(json); // 输出:{"name":"张三"},age 和 address 消失了
解决方案:
// 输出 null 值
String json = JSON.toJSONString(user,
JSONWriter.Feature.WriteMapNullValue);
// 输出:{"name":"张三","age":null,"address":null}
// 更精细的控制:null 转为空字符串/空数组
JSON.config(JSONWriter.Feature.WriteNullStringAsEmpty); // null 字符串变 ""
JSON.config(JSONWriter.Feature.WriteNullListAsEmpty); // null 集合变 []
坑6:枚举解析失败变 null
报错:不报错,但字段变成 null。
示例:
public enum Status {
NORMAL(1), LOCKED(2);
@JSONValue
private int code;
// 构造方法、getter
}
// JSON:{"status": "1"} ← 值是字符串
// 解析后 status 为 null
解决方案:升级到 Fastjson2 2.0.54+ 版本,已支持类型自动转换。
<dependency>
<groupId>com.alibaba.fastjson2</groupId>
<artifactId>fastjson2</artifactId>
<version>2.0.60</version>
</dependency>
三、快速排查表
| 现象 | 优先检查项 |
|---|---|
| 字段值为 null | ① 字段名是否匹配(驼峰/下划线)② 是否有无参构造器 ③ 是否有 public getter/setter |
| 类型转换异常 | ① 数字是否被写成字符串 ② 是否开启 SupportSmartMatch |
| 泛型取出来是 JSONObject | 是否用了 TypeReference(且带 {}) |
| 序列化栈溢出 | 是否存在双向引用(父子互相持有) |
| 日期输出是数字 | 是否配置了 WriteDateUseDateFormat |
| null 字段不见了 | 是否配置了 WriteMapNullValue |
四、Maven 依赖
<dependency>
<groupId>com.alibaba.fastjson2</groupId>
<artifactId>fastjson2</artifactId>
<version>2.0.60</version>
</dependency>
五、小结
Fastjson2 的核心使用原则:
- 字段名不匹配 →
@JSONField(name="xxx")或SupportSmartMatch - 泛型解析 → 必须用
TypeReference<...>() {} - 安全第一 → 保持 AutoType 关闭,升级到最新版本
- 循环引用 → 从设计上避免,或禁用循环引用检测
- 金额处理 → 用
String或Long,不要直接用float/double

