从 Fastjson1 迁移到 Fastjson2

为什么需要迁移?

Fastjson2(版本2.x)是 Fastjson1(1.2.x)的完全重写版,主要改进包括:

  • 安全性增强:默认禁用危险的反序列化特性,修复了多个高危漏洞
  • 性能提升:采用全新解析引擎,序列化/反序列化速度提升30%以上
  • API更简洁:废弃已过时方法,提供更直观的调用方式

第一步:替换依赖

Maven配置

<!-- 旧版 Fastjson1 -->
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version>1.2.83</version>
</dependency>

<!-- 新版 Fastjson2 -->
<dependency>
    <groupId>com.alibaba.fastjson2</groupId>
    <artifactId>fastjson2</artifactId>
    <version>2.0.61</version>
</dependency>

包路径变化com.alibaba.fastjsoncom.alibaba.fastjson2

第二步:核心API迁移对照

1. JSON序列化

Fastjson1写法:

User user = new User("张三", 25);
String json = JSON.toJSONString(user);

Fastjson2写法:

User user = new User("张三", 25);
String json = JSON.toJSONString(user);
// 方法名完全相同,无缝升级

2. JSON反序列化

Fastjson1写法:

String json = "{\"name\":\"李四\",\"age\":30}";
User user = JSON.parseObject(json, User.class);

Fastjson2写法:

String json = "{\"name\":\"李四\",\"age\":30}";
User user = JSON.parseObject(json, User.class);
// 依旧保持兼容,无需修改

3. JSONArray/JSONObject处理

Fastjson1写法:

JSONArray arr = JSON.parseArray("[1,2,3]");
JSONObject obj = JSON.parseObject("{\"key\":\"value\"}");

Fastjson2写法:

// 在 Fastjson2 中,JSONArray/JSONObject 依然存在且可用
// 但为了追求极致性能和类型安全,官方更推荐直接解析为 Java 原生集合(List/Map)或自定义对象。
List<Integer> list = JSON.parseArray("[1,2,3]", Integer.class);
Map<String, Object> map = JSON.parseObject("{\"key\":\"value\"}");

第三步:需要注意的改动

1. 日期格式化

旧写法(可能出问题):

JSON.toJSONStringWithDateFormat(date, "yyyy-MM-dd");

新写法(推荐):

 // 直接定义日期格式
String json = JSON.toJSONString(user, "yyyy-MM-dd");

// Fastjson2支持全局配置或注解驱动
@Data
public class User {
    @JSONField(format = "yyyy-MM-dd")
    private Date birthday;
}

2. 过滤/处理字段

旧写法:

SimplePropertyPreFilter filter = new SimplePropertyPreFilter(User.class, "name");

新写法:

  • 基础过滤:API 基本兼容,继续使用 SimplePropertyPreFilter 或 PropertyFilter。
// 依然需要传入过滤器对象
SimplePropertyPreFilter filter = new SimplePropertyPreFilter(User.class, "name");
JSON.toJSONString(user, filter);
  • 高级脱敏:使用 Fastjson2 新增的 BeforeFilter,可在序列化前拦截并修改字段值(如密码脱敏)。
  • 默认行为优化:Fastjson2 默认不输出 null 值和默认值(如 0),无需像 1.x 那样繁琐配置 WriteMapNullValue。

3. 安全性配置

Fastjson2 默认禁用 AutoType,天然防御反序列化漏洞。

  • 基础防护(推荐):直接使用 JSON.parseObject(json, User.class),避免使用无类型的 parse 方法。
  • 白名单机制(多态场景):如果业务必须支持 @type 字段,务必配置白名单:
ParserConfig config = new ParserConfig();
// 仅允许特定包下的类进行反序列化
config.addAccept("com.example.domain.");
JSON.parseObject(json, Object.class, config);
  • 终极防护(SafeMode):如果不需要任何自动类型特性,可开启安全模式彻底禁止 @type:
// 启动参数: -Dfastjson.parser.safeMode=true
// 或代码配置:
ParserConfig.getGlobalInstance().setSafeMode(true);

完整迁移示例

假设我们需要重构一个实体类解析功能:

// 旧代码(Fastjson1)
public class Fastjson1Demo {
    public static void main(String[] args) {
        String json = "{\"userId\":101,\"userName\":\"admin\",\"createTime\":\"2024-01-15\"}";

        // 使用JSONObject
        JSONObject obj = JSON.parseObject(json);
        long id = obj.getLong("userId");
        String name = obj.getString("userName");

        // 反序列化为对象
        User user = JSON.parseObject(json, User.class);
        System.out.println(user);
    }
}

// 新代码(Fastjson2)
public class Fastjson2Demo {
    public static void main(String[] args) {
        String json = "{\"userId\":101,\"userName\":\"admin\",\"createTime\":\"2024-01-15\"}";

        // 优先使用Map代替JSONObject
        Map<String, Object> map = JSON.parseObject(json);
        long id = ((Number)map.get("userId")).longValue();
        String name = (String) map.get("userName");

        // 反序列化不变
        User user = JSON.parseObject(json, User.class);
        System.out.println(user);
    }
}

// 实体类需要增加时间格式注解
@Data
public class User {
    private long userId;
    private String userName;
    @JSONField(format = "yyyy-MM-dd")
    private Date createTime;
}

Fastjson2 迁移检查清单

1. 依赖与包路径

  • 更新 Maven/Gradle 坐标
    • 移除 com.alibaba:fastjson
    • 新增 com.alibaba.fastjson2:fastjson2 (建议使用最新稳定版)
  • 全局替换 Import 路径
    • 查找替换:import com.alibaba.fastjsonimport com.alibaba.fastjson2
    • 注意:如果是 Spring Boot 项目,检查是否引入了 fastjson-http-message-converter 的对应 2.x 版本。

2. 代码适配 (核心)

  • 调整集合反序列化写法
    • 旧写法:JSON.parseObject(json, new TypeReference<List<User>>() {})
    • 新写法:JSON.parseArray(json, User.class) (Fastjson2 简化了泛型处理,优先用这个)
  • 审查 JSONObejct/JSONArray 的使用
    • 修正点:不需要全部改为 List/Map!
    • 行动:保留动态字段场景下的使用,但需修改导入包。仅在追求极致性能的核心链路中,才建议重构为 JavaBean 或 Map。
  • 修复 Bean 规范
    • 重点:Fastjson2 对 Java Bean 规范要求更严。确保实体类有无参构造函数,且字段非 final(除非有特殊配置)。Lombok 用户请确保 @Data 生效。

3. 安全与配置

  • 清理 AutoType 配置
    • 删除 ParserConfig.getGlobalInstance().setAutoTypeSupport(true) 等全局开启的代码。
    • 替代方案:如需多态支持,改用 config.addAccept("com.example.") 配置白名单。
  • 检查日期格式化
    • 行为变更:Fastjson2 默认将 Date 序列化为字符串(1.x 默认为时间戳)。
    • 行动:如果前端依赖时间戳,需添加 @JSONField(format = "millis") 或全局配置。如果只需普通日期,确认格式是否符合预期(如 yyyy-MM-dd HH:mm:ss)。

4. 验证与测试

  • 运行全量单元测试
    • 重点关注:空对象 {}、Null 值字段、嵌套过深的 JSON。
  • 对比输出结果
    • 检查序列化后的 JSON 字符串是否与 1.x 完全一致(特别是 Null 字段的处理,2.x 默认不输出 Null)。

总结: Fastjson2并非完全兼容旧版本,但核心API改动极小。只要注意 JSONObject/JSONArray 和安全性配置,大多数项目可在1小时内完成迁移。建议先在测试环境验证,再推广到生产系统。