一、引言:Jackson 为何是 Java 开发者的必修课?
Jackson 作为 Spring Boot 默认 JSON 解析库(含于 jackson-databind 依赖),解决了 Java 对象与 JSON 互转的核心痛点。其核心优势在于:
- 高性能:比 Gson 快 30%+,支持流式处理
- 灵活性:注解 + 配置双重控制序列化行为
- 兼容性:完美支持 Java 8 时间 API(需 jackson-datatype-jsr310)
- 线程安全:ObjectMapper 实例可全局复用(Spring 自动注入)
本文聚焦对象 / 字符串 / 集合 / 数组 / Map 转 JSON五大实战场景,每个案例均提供可直接复制的代码,并揭露 90% 开发者踩过的坑。
二、五大核心场景实战(附完整代码)
场景 1:Java 对象 <-> JSON(最常用)
核心 API:
ObjectMapper.writeValueAsString()(序列化)、readValue()(反序列化)
实战代码:
java
// 1. 定义实体类(含注解控制)
public class User {
@JsonProperty("user_id") // 自定义JSON字段名
private Long id;
private String name;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") // 日期格式化
private LocalDateTime createTime;
@JsonIgnore // 排除序列化
private String password;
// 必须有无参构造(反序列化必填)
public User() {}
// getter/setter省略
}
// 2. 序列化:对象→JSON
@Autowired
private ObjectMapper mapper; // Spring自动注入
public void objectToJson() throws JsonProcessingException {
User user = new User();
user.setId(1L);
user.setName("Jackson");
user.setCreateTime(LocalDateTime.now());
user.setPassword("123456"); // 会被@JsonIgnore排除
String json = mapper.writeValueAsString(user);
// 输出:{"user_id":1,"name":"Jackson","createTime":"2025-09-02 22:30:00"}
System.out.println(json);
}
// 3. 反序列化:JSON→对象
public void jsonToObject() throws JsonProcessingException {
String json = "{\"user_id\":1,\"name\":\"Jackson\",\"createTime\":\"2025-09-02 22:30:00\"}";
User user = mapper.readValue(json, User.class);
System.out.println(user.getName()); // 输出:Jackson
}
避坑点:
- 反序列化必须提供无参构造(否则报InvalidFormatException)
- 日期字段务必加timezone="GMT+8",否则默认 UTC 时差 8 小时
场景 2:JSON 字符串 <-> JsonNode(动态解析)
核心 API:readTree()(字符串→JsonNode)、path()(安全取值)
实战代码:
java
public void jsonNodeOperation() throws JsonProcessingException {
String json = "{\"code\":200,\"data\":{\"list\":[{\"id\":1,\"name\":\"Java\"}]}}";
// 1. 字符串转JsonNode
JsonNode rootNode = mapper.readTree(json);
// 2. 安全取值(避免NullPointerException)
int code = rootNode.path("code").asInt(); // 200
String name = rootNode.path("data")
.path("list")
.path(0)
.path("name")
.asText(); // Java
// 3. 修改节点值
((ObjectNode) rootNode).put("code", 201);
String modifiedJson = mapper.writeValueAsString(rootNode);
// 输出:{"code":201,"data":{"list":[{"id":1,"name":"Java"}]}}
}
优势:无需定义实体类,适合接口返回结构动态变化的场景。
场景 3:集合(List/Set) <-> JSON
核心痛点:Java 泛型擦除导致直接反序列化List<User>失败
解决方案:用TypeReference保留泛型信息
实战代码:
java
public void collectionToJson() throws JsonProcessingException {
// 1. List→JSON
List<User> userList = Arrays.asList(
new User(1L, "A"),
new User(2L, "B")
);
String listJson = mapper.writeValueAsString(userList);
// 输出:[{"user_id":1,"name":"A",...},{"user_id":2,"name":"B",...}]
// 2. JSON→List(关键:用TypeReference)
List<User> parsedList = mapper.readValue(
listJson,
new TypeReference<List<User>>() {} // 匿名内部类保留泛型
);
System.out.println(parsedList.size()); // 输出:2
}
避坑点:严禁用List.class作为readValue的类型参数,会导致元素被解析为LinkedHashMap而非User。
场景 4:数组 <-> JSON
核心 API:直接支持数组类型,无需额外配置
实战代码:
java
public void arrayToJson() throws JsonProcessingException {
// 1. 基本类型数组→JSON
int[] intArray = {1, 2, 3};
String intJson = mapper.writeValueAsString(intArray); // [1,2,3]
// 2. 对象数组→JSON
User[] userArray = {new User(1L, "A"), new User(2L, "B")};
String userJson = mapper.writeValueAsString(userArray);
// 输出:[{"user_id":1,"name":"A",...},{"user_id":2,"name":"B",...}]
// 3. JSON→对象数组
User[] parsedArray = mapper.readValue(userJson, User[].class);
System.out.println(parsedArray.length); // 输出:2
}
技巧:数组转 List 可结合Arrays.asList(parsedArray),但注意返回的是固定大小的 List。
场景 5:Map <-> JSON(键值对灵活转换)
核心特性:支持HashMap(无序)和LinkedHashMap(有序)
实战代码:
java
public void mapToJson() throws JsonProcessingException {
// 1. LinkedHashMap(保留插入顺序)→JSON
Map<String, Object> map = new LinkedHashMap<>();
map.put("id", 1);
map.put("name", "Jackson");
map.put("tags", Arrays.asList("JSON", "Java"));
String mapJson = mapper.writeValueAsString(map);
// 输出:{"id":1,"name":"Jackson","tags":["JSON","Java"]}
// 2. JSON→Map(泛型安全)
Map<String, Object> parsedMap = mapper.readValue(
mapJson,
new TypeReference<Map<String, Object>>() {}
);
List<String> tags = (List<String>) parsedMap.get("tags");
System.out.println(tags.get(0)); // 输出:JSON
}
避坑点:HashMap序列化后键的顺序不固定,需有序场景务必用LinkedHashMap。
三、核心配置避坑指南(全局 + 局部)
1. 日期格式化(解决时区差问题)
全局配置(application.yml):
yaml
spring:
jackson:
date-format: yyyy-MM-dd HH:mm:ss
time-zone: GMT+8 # 关键:避免UTC时差
serialization:
write-dates-as-timestamps: false # 禁用时间戳
局部配置(优先级高于全局):
java
@JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
private LocalDate birthday;
2. 空值处理(控制 null 字段是否序列化)
全局配置:
java
// 方式1:代码配置
mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); // 排除null字段
// 方式2:yml配置
spring.jackson.default-property-inclusion: non_null
局部配置:
java
@JsonInclude(JsonInclude.Include.ALWAYS) // 强制序列化该字段(即使null)
private String remark;
关键区别:手动设为null的字段会被序列化(如user.setRemark(null)),默认未赋值的null字段会被排除。
3. 泛型安全(TypeReference 原理)
TypeReference通过匿名内部类保留泛型信息,避免类型擦除:
java
// 错误写法(泛型丢失,元素为LinkedHashMap)
List<User> wrongList = mapper.readValue(json, List.class);
// 正确写法(泛型保留)
List<User> rightList = mapper.readValue(json, new TypeReference<List<User>>() {});
四、总结与进阶
- 核心工具类:ObjectMapper(全局复用)、JsonNode(动态解析)、TypeReference(泛型安全)
- 必避 3 大坑:日期时区差:加timezone="GMT+8"泛型擦除:用TypeReference空值失控:全局NON_NULL+ 局部ALWAYS
- 进阶扩展:自定义序列化器:实现StdSerializer(如枚举特殊处理)流式处理:JsonGenerator(超大 JSON 文件,内存友好)注解大全:@JsonAlias(多字段映射)、@JsonView(视图过滤)
感谢关注【AI码力】!