文章目录
系列文章:
替换 FastJSON:推荐 Jackson 及详细用法指南(含工具类 + 替换方案)
总结下Jackson 中的JsonNode,ObjectNode,ArrayNode的方法
JsonNode 和 ObjectNode的区别以及他们能互换吗
Jackson 使用问题记录(持续更新)
FastJSON 曾经是 Java 领域最流行的 JSON 序列化/反序列化库之一,但由于其历史安全问题频发、维护不积极等问题,越来越多公司开始禁用 FastJSON,并转向更稳定、安全、标准的替代库。
本文重点介绍推荐替代 FastJSON 的首选库 —— Jackson,并提供完整的使用示例、封装工具类以及从 FastJSON 迁移至 Jackson 的建议。
一、推荐替代库:Jackson
1、市面上主流JSON 解析库
以下是几个主流的 Java JSON 解析库,适合企业级项目替换使用:
名称 | 优点 | 缺点 | 推荐指数 |
---|---|---|---|
Jackson | 性能好、功能全、社区活跃、Spring Boot 默认使用 | 配置稍复杂,学习曲线略高 | ⭐⭐⭐⭐⭐ |
Gson | Google 出品,简单易用,API 友好 | 性能略逊于 Jackson,不支持流式解析大文件 | ⭐⭐⭐⭐ |
fastjson2(升级版) | FastJSON 官方新版本,修复部分问题 | 社区信任度下降,仍有兼容性/安全性顾虑 | ⭐⭐ |
moshi | 简洁现代,适合 Kotlin 和 Android | 功能较弱,不如 Jackson 强大 | ⭐⭐⭐ |
Yasson / JSON-B | Java EE 标准 JSON 绑定实现 | 使用较少,文档不多 | ⭐⭐ |
2、为什么选择 Jackson?
特性 | 描述 |
---|---|
性能优秀 | 支持流式解析,处理大文件效率高 |
社区活跃 | 官方维护频繁,文档丰富 |
Spring Boot 默认集成 | 几乎所有现代 Spring 项目都基于它 |
安全性更高 | 不像 FastJSON 存在自动类型转换等潜在风险 |
二、Maven 依赖(Jackson)
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.16.1</version>
</dependency>
⚠️ 注意:
jackson-databind
包含了core
和annotations
,一般只需引入此依赖即可。
三、Jackson 常用用法详解
1.Java 对象转 JSON 字符串
//Java 对象转 JSON 字符串
@Test
public void test1() throws JsonProcessingException {
ObjectMapper mapper = new ObjectMapper();
User user = new User("admin", "若依", 1L);
String json = mapper.writeValueAsString(user);
System.out.println(json);
// 输出: {"username":"admin","nickName":"若依","userId":1}
}
2.JSON 字符串转 Java 对象
//JSON 字符串转 Java 对象
@Test
public void test2() throws JsonProcessingException {
String json = "{\"username\":\"admin\",\"nickName\":\"若依\",\"userId\":1}";
ObjectMapper mapper = new ObjectMapper();
User user = mapper.readValue(json, User.class);
System.out.println(user.getUsername()); // admin
}
3.将 JSON 字符串转为 JSON 对象(JsonNode vs ObjectNode)
在使用 FastJSON 时,我们经常使用如下方式将字符串解析为 JSON 对象:
JSONObject jsonObject = JSON.parseObject(jsonString);
而在 Jackson 中,并没有 JSONObject
这个类,取而代之的是 JsonNode
和 ObjectNode
。理解这两个类的区别和使用方法,对于从 FastJSON 迁移到 Jackson 非常重要。
(1)JsonNode 与 ObjectNode 的区别
类名 | 类型 | 特点 |
---|---|---|
JsonNode |
只读对象 | 是 Jackson 提供的通用 JSON 节点抽象类,可以表示任意 JSON 值(对象、数组、字符串等) |
ObjectNode |
可写对象 | 是 JsonNode 的子类,用于构建或修改 JSON 对象,支持添加字段、嵌套结构等 |
✅ 简单来说:
- 如果你只是想解析并读取 JSON 数据,使用
JsonNode
- 如果你想创建或修改 JSON 内容,使用
ObjectNode
(2)将 JSON 字符串转为 JsonNode(类似 FastJSON 的 parseObject)
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
public class JsonNodeExample {
public static void main(String[] args) throws Exception {
String jsonStr = "{\n" +
" \"username\": \"admin\",\n" +
" \"userId\": 1,\n" +
" \"dept\": {\n" +
" \"deptId\": 103,\n" +
" \"deptName\": \"研发部门\"\n" +
" },\n" +
" \"roles\": [\n" +
" {\"roleId\": 1, \"roleName\": \"超级管理员\"},\n" +
" {\"roleId\": 2, \"roleName\": \"普通用户\"}\n" +
" ]\n" +
"}";
ObjectMapper mapper = new ObjectMapper();
JsonNode rootNode = mapper.readTree(jsonStr);
// 获取基本字段
String username = rootNode.get("username").asText();
int userId = rootNode.get("userId").asInt();
// 获取嵌套对象
JsonNode deptNode = rootNode.get("dept");
String deptName = deptNode.get("deptName").asText();
// 获取数组元素
JsonNode rolesNode = rootNode.get("roles");
for (JsonNode role : rolesNode) {
int roleId = role.get("roleId").asInt();
String roleName = role.get("roleName").asText();
System.out.println("Role ID: " + roleId + ", Name: " + roleName);
}
}
}
(3)常用 JsonNode 方法总结
方法 | 说明 |
---|---|
get("field") |
获取指定字段的子节点(返回类型为 JsonNode ) |
has("field") |
判断是否存在该字段 |
isValueNode() |
是否是值节点(如字符串、数字等) |
isObject() |
是否是对象节点 |
isArray() |
是否是数组节点 |
isNull() |
是否为 null |
asText() |
转为字符串 |
asInt() / asDouble() / asBoolean() |
转为基础类型 |
size() |
获取数组或对象中字段数量 |
elements() |
获取对象中所有字段的迭代器 |
iterator() |
获取数组中所有元素的迭代器 |
fieldNames() |
获取对象的所有字段名(Iterator) |
(4)创建新的 JSON 对象(使用 ObjectNode)
如果你需要构造一个新的 JSON 对象,而不是仅仅读取已有的 JSON,可以使用 ObjectNode
:
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
public class CreateJsonExample {
public static void main(String[] args) {
ObjectMapper mapper = new ObjectMapper();
ObjectNode userNode = mapper.createObjectNode();
userNode.put("username", "admin");
userNode.put("userId", 1);
userNode.put("status", true);
// 添加嵌套对象
ObjectNode deptNode = mapper.createObjectNode();
deptNode.put("deptId", 103);
deptNode.put("deptName", "研发部门");
userNode.set("dept", deptNode);
// 输出 JSON 字符串
System.out.println(userNode.toPrettyString());
}
}
输出结果:
{
"username" : "admin",
"userId" : 1,
"status" : true,
"dept" : {
"deptId" : 103,
"deptName" : "研发部门"
}
}
(5)FastJSON 与 Jackson 对应写法对照表
FastJSON 写法 | Jackson 替代写法 |
---|---|
JSONObject jsonObject = JSON.parseObject(jsonString); |
JsonNode jsonNode = objectMapper.readTree(jsonString); |
String name = jsonObject.getString("name"); |
String name = jsonNode.get("name").asText(); |
Integer age = jsonObject.getInteger("age"); |
int age = jsonNode.get("age").asInt(); |
JSONObject dept = jsonObject.getJSONObject("dept"); |
JsonNode dept = jsonNode.get("dept"); |
JSONArray roles = jsonObject.getJSONArray("roles"); |
JsonNode roles = jsonNode.get("roles"); |
jsonObject.containsKey("key") |
jsonNode.has("key") |
new JSONObject() |
ObjectNode objectNode = objectMapper.createObjectNode(); |
jsonObject.put("key", value) |
objectNode.put("key", value); |
(6)总结
功能 | 推荐使用类 |
---|---|
解析 JSON 字符串并读取字段 | JsonNode |
构建/修改 JSON 对象 | ObjectNode |
获取字段值 | .get("field").asText() |
判断字段是否存在 | .has("field") |
快速遍历对象字段 | .fieldNames() + 循环 |
快速遍历数组 | for (JsonNode node : arrayNode) |
4.List 转 JSON 数组
@Test
public void test5() throws JsonProcessingException {
List<User> users = Arrays.asList(new User("a", "A", 1), new User("b", "B", 2));
ObjectMapper mapper = new ObjectMapper();
String jsonArray = mapper.writeValueAsString(users);
System.out.println(jsonArray);
// [{"username":"a","nickName":"A","userId":1},{"username":"b","nickName":"B","userId":2}]
}
4.Map 转 JSON
@Test
public void test6() throws JsonProcessingException {
Map<String, Object> map = new HashMap<>();
map.put("name", "admin");
map.put("age", 25);
ObjectMapper mapper = new ObjectMapper();
String json = mapper.writeValueAsString(map);
System.out.println(json); // {"name":"admin","age":25}
}
5.自定义日期格式
@Test
public void test7() throws JsonProcessingException {
ObjectMapper mapper = new ObjectMapper();
mapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
User user = new User("admin", "若依", 1L);
user.setCreateTime(new Date());
String json = mapper.writeValueAsString(user);
System.out.println(json);
// {"username":"admin","createTime":"2025-05-13 15:00:00"}
}
6.忽略空字段(NULL 或 EMPTY)
@Test
public void test8() throws JsonProcessingException {
ObjectMapper mapper = new ObjectMapper();
mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); // 忽略 null 字段
User user = new User(null, "若依", null);
String json = mapper.writeValueAsString(user);
System.out.println(json); // {"nickName":"若依"}
}
四、封装 Jackson 工具类(带注释)
下面是一个完整的 JsonUtils
工具类,用于统一 JSON 操作入口,便于后续维护和更换底层实现。
package com.wenge.business;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import java.util.Collections;
import java.util.List;
import java.util.Map;
/**
* Jackson JSON 工具类
*/
public class JsonUtils {
private static final ObjectMapper mapper = new ObjectMapper();
static {
// 忽略未知字段,防止反序列化失败
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
// 忽略空对象不抛异常
mapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
// 支持 Java8 时间 API(LocalDate, LocalDateTime)
mapper.registerModule(new JavaTimeModule());
}
/**
* 对象转 JSON 字符串
*/
public static String toJson(Object obj) {
try {
return mapper.writeValueAsString(obj);
} catch (JsonProcessingException e) {
throw new RuntimeException("JSON serialize error", e);
}
}
/**
* JSON 字符串转 Java 对象
*/
public static <T> T fromJson(String json, Class<T> clazz) {
try {
return mapper.readValue(json, clazz);
} catch (JsonProcessingException e) {
throw new RuntimeException("JSON deserialize error", e);
}
}
/**
* JSON 字符串转复杂泛型对象(如 List<User>)
*/
public static <T> T fromJson(String json, TypeReference<T> typeReference) {
try {
return mapper.readValue(json, typeReference);
} catch (JsonProcessingException e) {
throw new RuntimeException("JSON deserialize error", e);
}
}
/**
* JSON 转 Map
*/
public static Map<String, Object> toMap(String json) {
if (json == null || json.isEmpty()) {
return Collections.emptyMap();
}
try {
return mapper.readValue(json, new TypeReference<Map<String, Object>>() {});
} catch (JsonProcessingException e) {
throw new RuntimeException("JSON to Map error", e);
}
}
/**
* JSON 转 List
*/
public static <T> List<T> toList(String json, Class<T> elementType) {
if (json == null || json.isEmpty()) {
return Collections.emptyList();
}
try {
return mapper.readValue(json, mapper.getTypeFactory().constructCollectionType(List.class, elementType));
} catch (JsonProcessingException e) {
throw new RuntimeException("JSON to List error", e);
}
}
/**
* 解析 JSON 字符串为 JsonNode
*/
public static JsonNode parseTree(String json) {
try {
return mapper.readTree(json);
} catch (JsonProcessingException e) {
throw new RuntimeException("JSON parse error", e);
}
}
/**
* 创建空的 ObjectNode
*/
public static ObjectNode createEmptyNode() {
return mapper.createObjectNode();
}
/**
*解析 JSON 字符串为 ObjectNode
**/
public static ObjectNode parseObjectNode(String jsonStr, ObjectMapper mapper) throws Exception {
JsonNode node = mapper.readTree(jsonStr);
if (node.isObject()) {
return (ObjectNode) node;
} else {
throw new IllegalArgumentException("JSON 字符串不是一个对象");
}
}
}
五、现有 FastJSON 调用方式一键替换成 Jackson 的写法
如果你之前使用的是 FastJSON,可以参考以下对应表进行快速替换。
FastJSON 方法 | Jackson 替代方法 | 示例 |
---|---|---|
JSON.toJSONString(obj) |
JsonUtils.toJson(obj) |
JsonUtils.toJson(user) |
JSON.parseObject(json, User.class) |
JsonUtils.fromJson(json, User.class) |
JsonUtils.fromJson(json, User.class) |
JSON.parseObject(json, new TypeReference<List<User>>() {}) |
JsonUtils.fromJson(json, new TypeReference<List<User>>() {}) |
JsonUtils.fromJson(json, new TypeReference<List<User>>() {}) |
JSON.parseObject(json, Map.class) |
JsonUtils.toMap(json) |
JsonUtils.toMap(json) |
JSON.parseArray(json, User.class) |
JsonUtils.toList(json, User.class) |
JsonUtils.toList(json, User.class) |
六、替换步骤建议
创建
JsonUtils
工具类(见上文)全局搜索替换关键字:
JSON.toJSONString(
→JsonUtils.toJson(
JSON.parseObject(
→JsonUtils.fromJson(
new TypeReference<
→new TypeReference<
JSON.parseArray(
→JsonUtils.toList(
测试验证:对关键模块进行单元测试或接口测试,确保序列化/反序列化结果一致。
逐步上线:先灰度发布部分功能,确认无误后再全面切换。
七、补充建议
建议 | 说明 |
---|---|
✅ 统一 JSON 处理入口 | 使用 JsonUtils 类统一调用,方便后期替换底层库 |
✅ 避免手动拼接 JSON | 易出错且难以维护,应始终使用序列化库生成 JSON |
✅ 开启 Jackson 的严格模式 | 防止非法字段、未知字段导致错误 |
✅ 使用泛型支持复杂结构 | 如 TypeReference 支持嵌套结构、集合类型 |
✅ 日志中打印 JSON 时使用 pretty print | 方便调试,可使用 mapper.writerWithDefaultPrettyPrinter() |
八、不同项目选择json库总结
场景 | 推荐库 |
---|---|
Spring Boot 项目 | ✅ Jackson |
微服务、分布式系统 | ✅ Jackson |
Android / Kotlin 项目 | ✅ Moshi |
简单工具类或小项目 | ✅ Gson |
已有项目迁出 FastJSON | ✅ fastjson2(过渡),尽快转 Jackson/Gson |