Spring 御用嫡子 Jackson 完全指南
作为一名 Java 开发工程师,拥抱主流 Jackson 是明智之选。本教程覆盖从基础配置到高级特性的完整迁移路径。
目录
- 环境准备与依赖
- Fastjson2 vs Jackson 核心概念对比
- Spring Boot 中 Jackson 的自动配置
- ObjectMapper 深度配置
- 核心注解大全
- 自定义序列化器 (Serializer)
- 自定义反序列化器 (Deserializer)
- JsonNode 树模型操作
- ObjectNode & ArrayNode 动态构建
- TypeReference 泛型处理
- 高级特性
- 性能优化
- 完整配置示例
1. 环境准备与依赖
Spring Boot 已经内置了 Jackson,只需确保 spring-boot-starter-web 依赖即可:
xml
<!-- pom.xml -->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.4.0</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Java 8 日期时间支持 -->
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
</dependency>
<!-- 参数名模块(可选,用于构造参数反序列化) -->
<dependency>
<groupId>com.fasterxml.jackson.module</groupId>
<artifactId>jackson-module-parameter-names</artifactId>
</dependency>
</dependencies>2. Fastjson2 vs Jackson 核心概念对比
| 概念 | Fastjson2 | Jackson |
|---|---|---|
| 核心类 | JSON, JSONObject, JSONArray | ObjectMapper, JsonNode, ArrayNode |
| 对象转JSON | JSON.toJSONString(obj) | objectMapper.writeValueAsString(obj) |
| JSON转对象 | JSON.parseObject(json, Class) | objectMapper.readValue(json, Class) |
| JSON转树 | JSON.parseObject(json) | objectMapper.readTree(json) |
| 动态构建 | JSONObject.put(key, value) | ObjectNode.put(key, value) |
| 泛型集合 | JSON.parseArray(json, Class) | objectMapper.readValue(json, new TypeReference<List<T>>(){}) |
| 忽略未知字段 | JSON.parseObject(json, Class, Feature.IgnoreNotMatch) | objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false) |
| 日期格式化 | @JSONField(format="yyyy-MM-dd") | @JsonFormat(pattern="yyyy-MM-dd") |
| 字段别名 | @JSONField(name="user_name") | @JsonProperty("user_name") |
| 忽略字段 | @JSONField(serialize=false) | @JsonIgnore |
3. Spring Boot 中 Jackson 的自动配置
Spring Boot 会自动配置 ObjectMapper,你可以通过以下方式获取:
java
@Service
public class UserService {
@Autowired
private ObjectMapper objectMapper; // Spring Boot 自动注入
public String toJson(Object obj) throws JsonProcessingException {
return objectMapper.writeValueAsString(obj);
}
}application.yml 配置
yaml
spring:
jackson:
# 日期格式
date-format: yyyy-MM-dd HH:mm:ss
time-zone: GMT+8
# 序列化特性
serialization:
write-dates-as-timestamps: false # 日期转字符串
indent-output: true # 美化输出
# 反序列化特性
deserialization:
fail-on-unknown-properties: false # 忽略未知字段
# 默认属性包含策略
default-property-inclusion: non_null # 忽略 null 值
# 属性命名策略
property-naming-strategy: SNAKE_CASE # 下划线命名4. ObjectMapper 深度配置
4.1 推荐配置方式(Spring Boot 2.x/3.x)
最佳实践:使用 Jackson2ObjectMapperBuilderCustomizer(不会覆盖 Spring Boot 默认配置)
java
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
@Configuration
public class JacksonConfig {
private static final String DEFAULT_DATE_TIME_PATTERN = "yyyy-MM-dd HH:mm:ss";
/**
* 推荐方式:使用 Jackson2ObjectMapperBuilderCustomizer
* 优点:不会覆盖 Spring Boot 的默认 ObjectMapper 配置,而是合并增强
*/
@Bean
@Order(Ordered.HIGHEST_PRECEDENCE + 1)
public Jackson2ObjectMapperBuilderCustomizer jacksonCustomizer() {
return builder -> {
// 注册 Java 8 日期时间模块
JavaTimeModule javaTimeModule = new JavaTimeModule();
javaTimeModule.addSerializer(LocalDateTime.class,
new LocalDateTimeSerializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_TIME_PATTERN)));
javaTimeModule.addDeserializer(LocalDateTime.class,
new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_TIME_PATTERN)));
builder.modules(javaTimeModule);
// 序列化特性
builder.featuresToEnable(
SerializationFeature.INDENT_OUTPUT // 格式化缩进
);
builder.featuresToDisable(
SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, // 日期转字符串
SerializationFeature.FAIL_ON_EMPTY_BEANS, // 忽略空 bean 错误
DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES // 忽略未知字段
);
// 全局忽略 null 值
builder.serializationInclusion(JsonInclude.Include.NON_NULL);
// 命名策略:下划线
builder.propertyNamingStrategy(PropertyNamingStrategies.SNAKE_CASE);
// 允许单引号
builder.featuresToEnable(JsonParser.Feature.ALLOW_SINGLE_QUOTES);
// 允许无引号字段名(非标准 JSON)
builder.featuresToEnable(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES);
};
}
}4.2 不推荐的方式(会覆盖默认配置)
java
// 不推荐!会丢失 Spring Boot 自动配置的模块和特性
@Bean
public ObjectMapper objectMapper() {
return new ObjectMapper()
.registerModule(new JavaTimeModule())
.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
}4.3 ObjectMapper 核心特性速查表
java
ObjectMapper mapper = new ObjectMapper();
// ========== 序列化特性 ==========
mapper.enable(SerializationFeature.INDENT_OUTPUT); // 格式化缩进
mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); // 日期转字符串
mapper.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS); // 忽略空 bean
mapper.enable(SerializationFeature.WRITE_ENUMS_USING_TO_STRING); // 枚举使用 toString
mapper.enable(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS); // Map 按键排序
mapper.disable(SerializationFeature.WRITE_NULL_MAP_VALUES); // 忽略 Map 中 null 值
// ========== 反序列化特性 ==========
mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); // 忽略未知字段
mapper.enable(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY); // "a" -> ["a"]
mapper.enable(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT); // "" -> null
mapper.disable(DeserializationFeature.FAIL_ON_NULL_FOR_PRIMITIVES); // null -> 默认值
// ========== 其他配置 ==========
mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); // 忽略 null 字段
mapper.setPropertyNamingStrategy(PropertyNamingStrategies.SNAKE_CASE); // 下划线命名
mapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
mapper.setTimeZone(TimeZone.getTimeZone("GMT+8"));5. 核心注解大全
5.1 基础映射注解
java
public class UserDTO {
// 字段别名(序列化和反序列化都生效)
@JsonProperty("user_name")
private String userName;
// 仅序列化时别名
@JsonProperty(value = "user_age", access = JsonProperty.Access.READ_ONLY)
private Integer age;
// 字段别名 + 指定顺序
@JsonProperty(value = "email_addr", index = 3)
private String email;
// 完全忽略该字段(序列化和反序列化)
@JsonIgnore
private String password;
// 仅在序列化时忽略
@JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
private String secretKey;
// 仅在反序列化时忽略
@JsonProperty(access = JsonProperty.Access.READ_ONLY)
private LocalDateTime createTime;
// 日期格式化
@JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
private LocalDate birthday;
// 数字格式化
@JsonFormat(shape = JsonFormat.Shape.STRING)
private Long id; // 防止前端精度丢失
// 枚举序列化为字符串
@JsonFormat(shape = JsonFormat.Shape.STRING)
private UserStatus status;
// 为空时不序列化
@JsonInclude(JsonInclude.Include.NON_EMPTY)
private List<String> tags;
// 为 null 时不序列化
@JsonInclude(JsonInclude.Include.NON_NULL)
private String description;
// 为默认值时不序列化(如 int 的 0, boolean 的 false)
@JsonInclude(JsonInclude.Include.NON_DEFAULT)
private int score;
// 自定义 null 值序列化行为
@JsonInclude(value = JsonInclude.Include.CUSTOM, valueFilter = MyFilter.class)
private String remark;
}5.2 类级别注解
java
// 忽略类中所有 null 字段
@JsonInclude(JsonInclude.Include.NON_NULL)
public class OrderDTO {
// 忽略多个指定字段(类级别)
@JsonIgnoreProperties({"internalId", "tempData"})
public static class InternalDTO { }
// 忽略未知字段(类级别,等同于全局配置)
@JsonIgnoreProperties(ignoreUnknown = true)
public static class FlexibleDTO { }
}
// 指定属性序列化顺序
@JsonPropertyOrder({"id", "name", "email", "age"})
public class OrderedDTO {
private String email;
private Long id;
private Integer age;
private String name;
}5.3 构造器和工厂方法
java
public class ImmutableUser {
private final Long id;
private final String name;
// 指定反序列化时使用的构造器
@JsonCreator
public ImmutableUser(
@JsonProperty("id") Long id,
@JsonProperty("name") String name) {
this.id = id;
this.name = name;
}
// 或者使用静态工厂方法
@JsonCreator
public static ImmutableUser of(
@JsonProperty("id") Long id,
@JsonProperty("name") String name) {
return new ImmutableUser(id, name);
}
}5.4 动态属性(@JsonAnyGetter / @JsonAnySetter)
java
public class FlexibleBean {
private String name;
// 存储动态属性
private Map<String, Object> extraProperties = new HashMap<>();
// 序列化时平铺输出 Map 中的属性
@JsonAnyGetter
public Map<String, Object> getExtraProperties() {
return extraProperties;
}
// 反序列化时接收未知属性
@JsonAnySetter
public void setExtraProperty(String key, Object value) {
this.extraProperties.put(key, value);
}
}
// 使用示例:
// {"name":"John","customField1":"value1","customField2":123}
// 反序列化后:name="John", extraProperties={"customField1":"value1","customField2":123}
// 序列化时:{"name":"John","customField1":"value1","customField2":123}5.5 JSON 视图(@JsonView)
java
public class Views {
public interface Public {}
public interface Internal extends Public {}
public interface Admin extends Internal {}
}
public class User {
@JsonView(Views.Public.class)
private Long id;
@JsonView(Views.Public.class)
private String username;
@JsonView(Views.Internal.class)
private String email;
@JsonView(Views.Admin.class)
private String password;
}
// Controller 中使用
@RestController
public class UserController {
@GetMapping("/users/{id}")
@JsonView(Views.Public.class)
public User getPublicUser(@PathVariable Long id) {
return userService.findById(id);
}
@GetMapping("/admin/users/{id}")
@JsonView(Views.Admin.class)
public User getAdminUser(@PathVariable Long id) {
return userService.findById(id);
}
}5.6 属性过滤器(@JsonFilter)
java
@JsonFilter("userFilter")
public class User {
private Long id;
private String name;
private String password;
private String email;
}
// 使用
SimpleFilterProvider filters = new SimpleFilterProvider();
filters.addFilter("userFilter",
SimpleBeanPropertyFilter.filterOutAllExcept("id", "name"));
ObjectMapper mapper = new ObjectMapper();
mapper.setFilterProvider(filters);
String json = mapper.writeValueAsString(user);6. 自定义序列化器 (Serializer)
6.1 基础自定义序列化器
java
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import java.io.IOException;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
/**
* 自定义 LocalDateTime 序列化器
* 将 LocalDateTime 序列化为 "yyyy年MM月dd日 HH时mm分ss秒" 格式
*/
public class CustomLocalDateTimeSerializer extends JsonSerializer<LocalDateTime> {
private static final DateTimeFormatter FORMATTER =
DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH时mm分ss秒");
@Override
public void serialize(LocalDateTime value, JsonGenerator gen, SerializerProvider serializers)
throws IOException {
if (value != null) {
gen.writeString(FORMATTER.format(value));
} else {
gen.writeNull();
}
}
}6.2 复杂对象自定义序列化器
java
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import java.io.IOException;
import java.math.BigDecimal;
import java.math.RoundingMode;
/**
* 金额序列化器:将分转为元,保留2位小数
*/
public class MoneySerializer extends JsonSerializer<Long> {
@Override
public void serialize(Long value, JsonGenerator gen, SerializerProvider serializers)
throws IOException {
if (value != null) {
BigDecimal yuan = BigDecimal.valueOf(value).divide(BigDecimal.valueOf(100), 2, RoundingMode.HALF_UP);
gen.writeString(yuan.toString());
} else {
gen.writeNull();
}
}
}
// 使用
public class Order {
@JsonSerialize(using = MoneySerializer.class)
private Long amount; // 单位为分,序列化后显示为元
}6.3 上下文感知的序列化器
java
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import java.io.IOException;
/**
* 脱敏序列化器:根据上下文决定是否脱敏
*/
public class MaskSerializer extends JsonSerializer<String> {
@Override
public void serialize(String value, JsonGenerator gen, SerializerProvider serializers)
throws IOException {
if (value == null || value.length() <= 4) {
gen.writeString(value);
return;
}
// 从上下文中获取是否脱敏的标志
Boolean shouldMask = (Boolean) serializers.getAttribute("shouldMask");
if (Boolean.TRUE.equals(shouldMask)) {
// 手机号脱敏:138****8888
if (value.length() == 11) {
gen.writeString(value.substring(0, 3) + "****" + value.substring(7));
} else {
// 通用脱敏:保留首尾各2位
gen.writeString(value.substring(0, 2) + "****" + value.substring(value.length() - 2));
}
} else {
gen.writeString(value);
}
}
}
// 使用
public class User {
@JsonSerialize(using = MaskSerializer.class)
private String phone;
@JsonSerialize(using = MaskSerializer.class)
private String idCard;
}
// 在 ObjectMapper 中设置属性
ObjectMapper mapper = new ObjectMapper();
mapper.setAttribute("shouldMask", true);6.4 注册自定义序列化器
java
// 方式1:注解注册(推荐,粒度最细)
public class User {
@JsonSerialize(using = CustomLocalDateTimeSerializer.class)
private LocalDateTime createTime;
}
// 方式2:Module 注册(全局生效)
@Bean
public Jackson2ObjectMapperBuilderCustomizer customizer() {
return builder -> {
SimpleModule module = new SimpleModule("customModule");
module.addSerializer(LocalDateTime.class, new CustomLocalDateTimeSerializer());
module.addSerializer(Long.class, new MoneySerializer());
builder.modules(module);
};
}
// 方式3:ObjectMapper 直接注册(不推荐在 Spring Boot 中使用)
ObjectMapper mapper = new ObjectMapper();
SimpleModule module = new SimpleModule();
module.addSerializer(LocalDateTime.class, new CustomLocalDateTimeSerializer());
mapper.registerModule(module);7. 自定义反序列化器 (Deserializer)
7.1 基础自定义反序列化器
java
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.JsonNode;
import java.io.IOException;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
/**
* 自定义 LocalDateTime 反序列化器
*/
public class CustomLocalDateTimeDeserializer extends JsonDeserializer<LocalDateTime> {
private static final DateTimeFormatter FORMATTER =
DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH时mm分ss秒");
@Override
public LocalDateTime deserialize(JsonParser p, DeserializationContext ctxt)
throws IOException, JsonProcessingException {
String text = p.getValueAsString();
if (text == null || text.isEmpty()) {
return null;
}
return LocalDateTime.parse(text, FORMATTER);
}
}7.2 复杂对象自定义反序列化器
java
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ArrayNode;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
/**
* 金额反序列化器:将元转为分
*/
public class MoneyDeserializer extends JsonDeserializer<Long> {
@Override
public Long deserialize(JsonParser p, DeserializationContext ctxt)
throws IOException, JsonProcessingException {
String text = p.getValueAsString();
if (text == null || text.isEmpty()) {
return null;
}
// 将元转为分
return new BigDecimal(text).multiply(BigDecimal.valueOf(100)).longValue();
}
}
/**
* 复杂对象反序列化器示例
*/
public class UserDeserializer extends JsonDeserializer<User> {
@Override
public User deserialize(JsonParser p, DeserializationContext ctxt)
throws IOException, JsonProcessingException {
JsonNode node = p.getCodec().readTree(p);
User user = new User();
// 手动解析每个字段
if (node.has("user_id")) {
user.setId(node.get("user_id").asLong());
}
if (node.has("user_name")) {
user.setName(node.get("user_name").asText());
}
// 处理嵌套对象
if (node.has("address")) {
JsonNode addressNode = node.get("address");
Address address = new Address();
address.setCity(addressNode.get("city").asText());
address.setStreet(addressNode.get("street").asText());
user.setAddress(address);
}
// 处理数组
if (node.has("roles")) {
List<String> roles = new ArrayList<>();
ArrayNode rolesNode = (ArrayNode) node.get("roles");
for (JsonNode roleNode : rolesNode) {
roles.add(roleNode.asText());
}
user.setRoles(roles);
}
return user;
}
}7.3 带泛型的反序列化器(ContextualDeserializer)
java
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.BeanProperty;
import com.fasterxml.jackson.databind.ContextualDeserializer;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.node.ObjectNode;
import java.io.IOException;
/**
* 通用包装器反序列化器,支持泛型
*/
public class WrapperDeserializer extends JsonDeserializer<Wrapper<?>> implements ContextualDeserializer {
private Class<?> wrappedType;
public WrapperDeserializer() {
// 默认构造器
}
private WrapperDeserializer(Class<?> wrappedType) {
this.wrappedType = wrappedType;
}
@Override
public JsonDeserializer<?> createContextual(DeserializationContext ctxt, BeanProperty property)
throws JsonMappingException {
// 获取泛型参数类型
if (property != null) {
Class<?> type = property.getType().containedType(0).getRawClass();
return new WrapperDeserializer(type);
}
return this;
}
@Override
public Wrapper<?> deserialize(JsonParser p, DeserializationContext ctxt)
throws IOException, JsonProcessingException {
ObjectNode node = p.getCodec().readTree(p);
Wrapper<Object> wrapper = new Wrapper<>();
if (node.has("code")) {
wrapper.setCode(node.get("code").asInt());
}
if (node.has("message")) {
wrapper.setMessage(node.get("message").asText());
}
if (node.has("data") && wrappedType != null) {
Object data = p.getCodec().treeToValue(node.get("data"), wrappedType);
wrapper.setData(data);
}
return wrapper;
}
}7.4 注册自定义反序列化器
java
// 方式1:注解注册
public class User {
@JsonDeserialize(using = CustomLocalDateTimeDeserializer.class)
private LocalDateTime createTime;
@JsonDeserialize(using = MoneyDeserializer.class)
private Long amount;
}
// 方式2:Module 注册
@Bean
public Jackson2ObjectMapperBuilderCustomizer deserializerCustomizer() {
return builder -> {
SimpleModule module = new SimpleModule("customDeserializerModule");
module.addDeserializer(LocalDateTime.class, new CustomLocalDateTimeDeserializer());
module.addDeserializer(Long.class, new MoneyDeserializer());
builder.modules(module);
};
}8. JsonNode 树模型操作
JsonNode 是 Jackson 的树模型核心,类似于 Fastjson2 的 JSONObject/JSONArray。
8.1 解析 JSON 为树
java
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
public class JsonNodeDemo {
@Autowired
private ObjectMapper objectMapper;
public void parseJson() throws JsonProcessingException {
String json = """
{
"name": "John",
"age": 30,
"address": {
"city": "Beijing",
"street": "Chaoyang"
},
"hobbies": ["reading", "coding", "gaming"],
"isActive": true,
"score": 95.5
}
""";
// 解析为 JsonNode 树
JsonNode rootNode = objectMapper.readTree(json);
// ========== 读取基础类型 ==========
String name = rootNode.get("name").asText(); // "John"
int age = rootNode.get("age").asInt(); // 30
double score = rootNode.get("score").asDouble(); // 95.5
boolean isActive = rootNode.get("isActive").asBoolean(); // true
// ========== 读取嵌套对象 ==========
JsonNode addressNode = rootNode.get("address");
String city = addressNode.get("city").asText(); // "Beijing"
String street = addressNode.get("street").asText(); // "Chaoyang"
// 或使用 path() 避免 NPE(找不到返回 MissingNode)
String city2 = rootNode.path("address").path("city").asText(); // "Beijing"
String unknown = rootNode.path("nonexistent").asText(); // "" (空字符串)
// ========== 读取数组 ==========
JsonNode hobbiesNode = rootNode.get("hobbies");
if (hobbiesNode.isArray()) {
for (JsonNode hobby : hobbiesNode) {
System.out.println(hobby.asText());
}
}
// 获取数组长度
int arraySize = hobbiesNode.size(); // 3
// 通过索引获取
String firstHobby = hobbiesNode.get(0).asText(); // "reading"
}
}8.2 安全读取(带默认值)
java
public void safeRead(JsonNode rootNode) {
// asXxx() 的重载版本支持默认值
String name = rootNode.path("name").asText("Unknown"); // 不存在返回 "Unknown"
int age = rootNode.path("age").asInt(0); // 不存在返回 0
double score = rootNode.path("score").asDouble(0.0); // 不存在返回 0.0
boolean active = rootNode.path("isActive").asBoolean(false); // 不存在返回 false
// 检查节点是否存在
if (rootNode.has("name")) {
// 存在
}
// 检查是否为 null 节点
if (rootNode.get("name").isNull()) {
// 值为 null
}
// 检查节点类型
JsonNode node = rootNode.get("value");
if (node.isTextual()) { /* 字符串 */ }
if (node.isNumber()) { /* 数字 */ }
if (node.isBoolean()) { /* 布尔 */ }
if (node.isArray()) { /* 数组 */ }
if (node.isObject()) { /* 对象 */ }
if (node.isMissingNode()) { /* 缺失节点 */ }
if (node.isNull()) { /* null */ }
}8.3 使用 at() 进行路径导航(JSON Pointer)
java
public void pathNavigation() throws JsonProcessingException {
String json = """
{
"store": {
"book": [
{"title": "Java", "price": 50},
{"title": "Python", "price": 40}
],
"bicycle": {"color": "red", "price": 200}
}
}
""";
JsonNode root = objectMapper.readTree(json);
// 使用 at() 和 JSON Pointer 语法
JsonNode firstBookTitle = root.at("/store/book/0/title"); // "Java"
JsonNode bicycleColor = root.at("/store/bicycle/color"); // "red"
// 路径不存在返回 MissingNode
JsonNode notFound = root.at("/store/food/0/name");
System.out.println(notFound.isMissingNode()); // true
}8.4 遍历 JsonNode
java
public void traverse(JsonNode rootNode) {
// 遍历对象的所有字段
Iterator<String> fieldNames = rootNode.fieldNames();
while (fieldNames.hasNext()) {
String fieldName = fieldNames.next();
JsonNode fieldValue = rootNode.get(fieldName);
System.out.println(fieldName + " = " + fieldValue);
}
// 遍历对象的所有字段(包含 key-value)
Iterator<Map.Entry<String, JsonNode>> fields = rootNode.fields();
while (fields.hasNext()) {
Map.Entry<String, JsonNode> entry = fields.next();
System.out.println(entry.getKey() + " = " + entry.getValue());
}
// 遍历数组
for (JsonNode element : rootNode) {
System.out.println(element);
}
// 递归遍历所有节点
traverseRecursive(rootNode, "");
}
private void traverseRecursive(JsonNode node, String path) {
if (node.isObject()) {
node.fields().forEachRemaining(entry -> {
traverseRecursive(entry.getValue(), path + "/" + entry.getKey());
});
} else if (node.isArray()) {
for (int i = 0; i < node.size(); i++) {
traverseRecursive(node.get(i), path + "[" + i + "]");
}
} else {
System.out.println(path + " = " + node.asText());
}
}8.5 JsonNode 与 POJO 互转
java
public void convertBetweenNodeAndPojo() throws JsonProcessingException {
// POJO -> JsonNode
User user = new User(1L, "John", "john@example.com");
JsonNode userNode = objectMapper.valueToTree(user);
// JsonNode -> POJO
User parsedUser = objectMapper.treeToValue(userNode, User.class);
// JsonNode -> 带泛型的集合
String jsonArray = "[{"name":"John"},{"name":"Jane"}]";
JsonNode arrayNode = objectMapper.readTree(jsonArray);
List<User> users = objectMapper.convertValue(arrayNode, new TypeReference<List<User>>() {});
// JsonNode -> Map
Map<String, Object> map = objectMapper.convertValue(userNode, new TypeReference<Map<String, Object>>() {});
}9. ObjectNode & ArrayNode 动态构建
9.1 创建和修改 ObjectNode
java
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
public class ObjectNodeDemo {
@Autowired
private ObjectMapper objectMapper;
public ObjectNode buildObject() {
// 创建空对象
ObjectNode rootNode = objectMapper.createObjectNode();
// 添加基础类型字段
rootNode.put("name", "John");
rootNode.put("age", 30);
rootNode.put("score", 95.5);
rootNode.put("isActive", true);
rootNode.putNull("deletedAt"); // null 值
// 添加嵌套对象
ObjectNode addressNode = rootNode.putObject("address");
addressNode.put("city", "Beijing");
addressNode.put("street", "Chaoyang");
// 添加数组
ArrayNode hobbiesNode = rootNode.putArray("hobbies");
hobbiesNode.add("reading");
hobbiesNode.add("coding");
hobbiesNode.add("gaming");
// 添加已存在的 JsonNode
ObjectNode extraNode = objectMapper.createObjectNode();
extraNode.put("key1", "value1");
rootNode.set("extra", extraNode); // 2.4+ 使用 set 替代 put(String, JsonNode)
// 添加 POJO
User user = new User(1L, "Jane");
rootNode.putPOJO("user", user);
// 输出 JSON
String json = rootNode.toString();
// 美化输出
String prettyJson = rootNode.toPrettyString();
return rootNode;
}
public void modifyObject(ObjectNode rootNode) {
// 修改字段
rootNode.put("name", "Jane"); // 覆盖原有值
// 替换节点
ObjectNode newAddress = objectMapper.createObjectNode();
newAddress.put("city", "Shanghai");
rootNode.replace("address", newAddress);
// 删除字段
rootNode.remove("deletedAt");
// 批量删除
rootNode.remove(Arrays.asList("extra", "user"));
// 保留指定字段,删除其他
rootNode.retain("name", "age");
// 删除所有字段
// rootNode.removeAll();
// 合并另一个 ObjectNode
ObjectNode other = objectMapper.createObjectNode();
other.put("newField", "newValue");
rootNode.setAll(other);
}
}9.2 创建和修改 ArrayNode
java
public ArrayNode buildArray() {
// 创建空数组
ArrayNode arrayNode = objectMapper.createArrayNode();
// 添加元素
arrayNode.add("string");
arrayNode.add(123);
arrayNode.add(true);
arrayNode.add(45.67);
arrayNode.addNull();
// 添加对象
ObjectNode obj = objectMapper.createObjectNode();
obj.put("id", 1);
arrayNode.add(obj);
// 添加数组
ArrayNode innerArray = objectMapper.createArrayNode();
innerArray.add(1);
innerArray.add(2);
arrayNode.add(innerArray);
// 在指定位置插入
arrayNode.insert(0, "first"); // 在索引0插入
// 删除元素
arrayNode.remove(0); // 删除索引0的元素
// 清空数组
// arrayNode.removeAll();
// 批量添加
ArrayNode anotherArray = objectMapper.createArrayNode();
anotherArray.add("a");
anotherArray.add("b");
arrayNode.addAll(anotherArray);
return arrayNode;
}9.3 与 Fastjson2 的对比
java
// ========== Fastjson2 风格 ==========
JSONObject jsonObject = new JSONObject();
jsonObject.put("name", "John");
jsonObject.put("age", 30);
JSONArray jsonArray = new JSONArray();
jsonArray.add("reading");
jsonArray.add("coding");
jsonObject.put("hobbies", jsonArray);
String json = jsonObject.toJSONString();
// ========== Jackson 风格 ==========
ObjectNode objectNode = objectMapper.createObjectNode();
objectNode.put("name", "John");
objectNode.put("age", 30);
ArrayNode arrayNode = objectMapper.createArrayNode();
arrayNode.add("reading");
arrayNode.add("coding");
objectNode.set("hobbies", arrayNode);
String json = objectNode.toString();10. TypeReference 泛型处理
10.1 基础泛型反序列化
java
import com.fasterxml.jackson.core.type.TypeReference;
public class TypeReferenceDemo {
@Autowired
private ObjectMapper objectMapper;
public void genericDeserialize() throws JsonProcessingException {
String json = "[{"name":"John","age":30},{"name":"Jane","age":25}]";
// 错误:无法直接获取泛型类型
// List<User> users = objectMapper.readValue(json, List.class);
// 正确:使用 TypeReference
List<User> users = objectMapper.readValue(json, new TypeReference<List<User>>() {});
// 泛型包装类
String responseJson = """
{
"code": 200,
"message": "success",
"data": {"id": 1, "name": "John"}
}
""";
ApiResponse<User> response = objectMapper.readValue(
responseJson,
new TypeReference<ApiResponse<User>>() {}
);
}
// 泛型方法封装
public <T> T parseJson(String json, TypeReference<T> typeReference) throws JsonProcessingException {
return objectMapper.readValue(json, typeReference);
}
public <T> List<T> parseList(String json, Class<T> clazz) throws JsonProcessingException {
return objectMapper.readValue(json, objectMapper.getTypeFactory()
.constructCollectionType(List.class, clazz));
}
public <K, V> Map<K, V> parseMap(String json, Class<K> keyClass, Class<V> valueClass)
throws JsonProcessingException {
return objectMapper.readValue(json, objectMapper.getTypeFactory()
.constructMapType(Map.class, keyClass, valueClass));
}
}10.2 使用 JavaType(动态类型)
java
public void dynamicType() throws JsonProcessingException {
String json = "[{"name":"John"},{"name":"Jane"}]";
// 使用 TypeFactory 构建动态类型
JavaType listType = objectMapper.getTypeFactory()
.constructCollectionType(List.class, User.class);
List<User> users = objectMapper.readValue(json, listType);
// 复杂泛型:Map<String, List<User>>
JavaType complexType = objectMapper.getTypeFactory()
.constructMapType(Map.class,
objectMapper.constructType(String.class),
objectMapper.getTypeFactory().constructCollectionType(List.class, User.class));
// 泛型包装类动态构建
JavaType responseType = objectMapper.getTypeFactory()
.constructParametricType(ApiResponse.class, User.class);
ApiResponse<User> response = objectMapper.readValue(json, responseType);
}11. 高级特性
11.1 Mix-in 注解(为第三方类添加注解)
java
// 第三方类(无法修改源码)
public class ThirdPartyClass {
private String name;
private String internalField;
// ...
}
// 定义 Mix-in 接口
@JsonIgnoreProperties("internalField")
public abstract class ThirdPartyClassMixin {
@JsonProperty("user_name")
abstract String getName();
}
// 注册 Mix-in
@Bean
public Jackson2ObjectMapperBuilderCustomizer mixinCustomizer() {
return builder -> builder.mixIn(ThirdPartyClass.class, ThirdPartyClassMixin.class);
}11.2 自定义属性命名策略
java
// 实现自定义命名策略
public class CustomNamingStrategy extends PropertyNamingStrategies.NamingBase {
@Override
public String translate(String input) {
// 自定义转换逻辑:全部大写
return input.toUpperCase();
}
}
// 注册
@Bean
public Jackson2ObjectMapperBuilderCustomizer namingCustomizer() {
return builder -> builder.propertyNamingStrategy(new CustomNamingStrategy());
}11.3 处理循环引用
java
// 方式1:使用 @JsonIdentityInfo
@JsonIdentityInfo(
generator = ObjectIdGenerators.PropertyGenerator.class,
property = "id"
)
public class Department {
private Long id;
private String name;
private List<Employee> employees;
}
public class Employee {
private Long id;
private String name;
private Department department; // 循环引用
}
// 方式2:使用 @JsonManagedReference / @JsonBackReference
public class Department {
private Long id;
private String name;
@JsonManagedReference // 主引用(序列化)
private List<Employee> employees;
}
public class Employee {
private Long id;
private String name;
@JsonBackReference // 反向引用(不序列化)
private Department department;
}11.4 自定义模块(Module)
java
public class CustomModule extends SimpleModule {
public CustomModule() {
super("customModule", new Version(1, 0, 0, null, null, null));
// 注册序列化器
addSerializer(LocalDateTime.class, new CustomLocalDateTimeSerializer());
addSerializer(Long.class, new MoneySerializer());
// 注册反序列化器
addDeserializer(LocalDateTime.class, new CustomLocalDateTimeDeserializer());
addDeserializer(Long.class, new MoneyDeserializer());
// 注册 Mix-in
setMixInAnnotation(ThirdPartyClass.class, ThirdPartyClassMixin.class);
}
@Override
public void setupModule(SetupContext context) {
super.setupModule(context);
// 更复杂的配置
}
}
// 注册模块
@Bean
public Jackson2ObjectMapperBuilderCustomizer moduleCustomizer() {
return builder -> builder.modules(new CustomModule());
}12. 性能优化
12.1 Afterburner 模块(字节码优化)
xml
<dependency>
<groupId>com.fasterxml.jackson.module</groupId>
<artifactId>jackson-module-afterburner</artifactId>
</dependency>java
@Bean
public Jackson2ObjectMapperBuilderCustomizer afterburnerCustomizer() {
return builder -> builder.modules(new AfterburnerModule());
}12.2 Blackbird 模块(Afterburner 的替代者,推荐)
xml
<dependency>
<groupId>com.fasterxml.jackson.module</groupId>
<artifactId>jackson-module-blackbird</artifactId>
</dependency>java
@Bean
public Jackson2ObjectMapperBuilderCustomizer blackbirdCustomizer() {
return builder -> builder.modules(new BlackbirdModule());
}12.3 ObjectMapper 重用
java
// ObjectMapper 是线程安全的,应该作为单例使用
@Configuration
public class ObjectMapperHolder {
private static ObjectMapper objectMapper;
@Autowired
public void setObjectMapper(ObjectMapper mapper) {
ObjectMapperHolder.objectMapper = mapper;
}
public static ObjectMapper get() {
return objectMapper;
}
}12.4 禁用不需要的特性
java
@Bean
public Jackson2ObjectMapperBuilderCustomizer performanceCustomizer() {
return builder -> {
builder.featuresToDisable(
SerializationFeature.FAIL_ON_EMPTY_BEANS,
DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES,
MapperFeature.DEFAULT_VIEW_INCLUSION
);
};
}13. 完整配置示例
java
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer;
import com.fasterxml.jackson.module.blackbird.BlackbirdModule;
import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import java.math.BigInteger;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
@Configuration
public class JacksonGlobalConfig {
private static final String DEFAULT_DATE_PATTERN = "yyyy-MM-dd";
private static final String DEFAULT_TIME_PATTERN = "HH:mm:ss";
private static final String DEFAULT_DATE_TIME_PATTERN = "yyyy-MM-dd HH:mm:ss";
@Bean
@Order(Ordered.HIGHEST_PRECEDENCE)
public Jackson2ObjectMapperBuilderCustomizer jacksonCustomizer() {
return builder -> {
// ========== 1. 注册日期时间模块 ==========
JavaTimeModule javaTimeModule = new JavaTimeModule();
// LocalDateTime
javaTimeModule.addSerializer(LocalDateTime.class,
new LocalDateTimeSerializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_TIME_PATTERN)));
javaTimeModule.addDeserializer(LocalDateTime.class,
new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_TIME_PATTERN)));
// LocalDate
javaTimeModule.addSerializer(LocalDate.class,
new LocalDateSerializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_PATTERN)));
javaTimeModule.addDeserializer(LocalDate.class,
new LocalDateDeserializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_PATTERN)));
// LocalTime
javaTimeModule.addSerializer(LocalTime.class,
new LocalTimeSerializer(DateTimeFormatter.ofPattern(DEFAULT_TIME_PATTERN)));
javaTimeModule.addDeserializer(LocalTime.class,
new LocalTimeDeserializer(DateTimeFormatter.ofPattern(DEFAULT_TIME_PATTERN)));
builder.modules(javaTimeModule);
// ========== 2. 序列化特性 ==========
builder.featuresToEnable(
SerializationFeature.INDENT_OUTPUT // 格式化缩进(开发环境可开启)
);
builder.featuresToDisable(
SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, // 日期转字符串
SerializationFeature.FAIL_ON_EMPTY_BEANS // 忽略空 bean 错误
);
// ========== 3. 反序列化特性 ==========
builder.featuresToDisable(
DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES // 忽略未知字段
);
// ========== 4. 全局包含策略 ==========
builder.serializationInclusion(JsonInclude.Include.NON_NULL);
// ========== 5. 命名策略 ==========
builder.propertyNamingStrategy(PropertyNamingStrategies.SNAKE_CASE);
// ========== 6. 允许非标准 JSON ==========
builder.featuresToEnable(
JsonParser.Feature.ALLOW_SINGLE_QUOTES, // 允许单引号
JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES // 允许无引号字段名
);
// ========== 7. 自定义模块 ==========
SimpleModule customModule = new SimpleModule("customModule");
// 将 Long 和 BigInteger 序列化为字符串,防止前端精度丢失
customModule.addSerializer(Long.class, new ToStringSerializer());
customModule.addSerializer(Long.TYPE, new ToStringSerializer());
customModule.addSerializer(BigInteger.class, new ToStringSerializer());
builder.modules(customModule);
// ========== 8. 性能优化模块 ==========
builder.modules(new BlackbirdModule());
};
}
}