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 对比?

对比项Fastjson1Fastjson2
包名com.alibaba.fastjsoncom.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 的核心使用原则:

  1. 字段名不匹配@JSONField(name="xxx")SupportSmartMatch
  2. 泛型解析 → 必须用 TypeReference<...>() {}
  3. 安全第一 → 保持 AutoType 关闭,升级到最新版本
  4. 循环引用 → 从设计上避免,或禁用循环引用检测
  5. 金额处理 → 用 StringLong,不要直接用 float/double