Jackson详解
本文最后更新于:6 分钟前
引言
在现代 Java 开发中,JSON 作为最常用的数据交换格式之一,其处理能力直接影响系统的效率与稳定性。Jackson 作为 Java 生态中最主流的 JSON 处理库,凭借其高性能、强大的注解支持、灵活的模块机制和良好的 Spring Boot 集成能力,已成为开发者的首选。本篇文章将系统梳理 Jackson 的核心模块、三种编程模型、关键注解用法及高级特性,并结合实际项目介绍如何进行深度配置与封装工具类,以容应对各种 JSON 序列化/反序列化的复杂场景,提升项目开发效率与健壮性。
基础
核心模块
Jackson 核心由以下三个主要模块组成:
jackson-core(核心解析器)
- 主要提供低级别的 Streaming API(基于
JsonParser
/JsonGenerator
)。 - 面向字符流进行高速处理,适合大数据量或高性能场景。
- 不包含对象映射能力,主要用于读取、写入 JSON token。
jackson-databind(数据绑定)
- 基于 core,提供高级数据绑定能力(POJO ↔ JSON)。
- 提供核心类:
ObjectMapper
,是 Jackson 的核心入口。 - 支持注解、泛型、自定义序列化等。
jackson-annotations(注解模块)
- 定义 Jackson 支持的注解元数据(如
@JsonProperty
、@JsonIgnore
等)。 - 不包含任何实现逻辑,仅提供注解定义。
编程模型
Jackson 提供三种编程模型,适配不同复杂度与性能需求
Streaming API(低级流式 API)
使用:
JsonParser
、JsonGenerator
。优点:性能最高,占用内存最小。
缺点:编程复杂,手动处理 token
示例:
1
2
3
4
5
6JsonFactory factory = new JsonFactory();
JsonParser parser = factory.createParser(jsonStr);
while (!parser.isClosed()) {
JsonToken token = parser.nextToken();
// 自行处理键值
}
Tree Model(中级树形模型)
使用:
JsonNode
、ObjectMapper.readTree()
、writeTree()
。优点:动态结构处理方便,类似 DOM。
缺点:性能中等,适合不确定结构的 JSON。
示例:
1
2JsonNode rootNode = objectMapper.readTree(jsonStr);
String name = rootNode.get("name").asText();
Data Binding(高级数据绑定)
使用:
ObjectMapper.readValue()
、writeValueAsString()
。优点:开发效率高,自动映射 POJO,最常用方式。
缺点:对于复杂结构需要借助注解、自定义序列化处理。
示例:
1
2User user = objectMapper.readValue(jsonStr, User.class);
String json = objectMapper.writeValueAsString(user);
中级树形模型
简介
中级树形模型(Tree Model)是 Jackson 提供的中间层 JSON 操作方式,它介于 ObjectMapper
的数据绑定和 Streaming API(Token 流式处理)之间。
它使用 JsonNode
表示整个 JSON 树,可以在不知道 POJO 的情况下灵活访问 JSON 结构,适用于结构不确定、动态 JSON 的处理。
适用场景
- JSON 字段不固定。
- 只想读取部分字段。
- JSON 嵌套层级很深。
- 需要手动构建或合并 JSON。
(对于高性能、低内存或已知结构、POJO 可用的场景则使用 Streaming API 或 Data Binding 更好)
核心类
JsonNode
: 抽象类,所有 JSON 节点的基类。ObjectNode
:表示 JSON 对象{}
。ArrayNode
:表示 JSON 数组[]
。TextNode、NumericNode、BooleanNode
:表示具体值(如字符串、数值、布尔等)。ObjectMapper.readTree()
:将 JSON 读取为JsonNode
。ObjectMapper.writeValueAsString(JsonNode)
:JsonNode
转 JSON 字符串。
API
读取 JSON
创建
JsonNode
:1
2
3
4String json = "{\"id\":1,\"name\":\"张三\",\"tags\":[\"a\",\"b\"]}";
ObjectMapper mapper = new ObjectMapper();
JsonNode root = mapper.readTree(json);以指定的数据类型获取字段:
1
2
3int id = root.get("id").asInt(); // 1
String name = root.get("name").asText(); // 张三
JsonNode tags = root.get("tags"); // ArrayNode遍历数组:
1
2
3for (JsonNode tagNode : tags) {
System.out.println(tagNode.asText()); // a, b
}安全访问(避免空指针):
1
String nickname = root.path("nickname").asText("默认昵称"); // 不存在时返回默认值
构建 JsonNode
手动创建 JSON 对象
1
2
3
4
5
6
7
8
9
10
11
12ObjectMapper mapper = new ObjectMapper();
ObjectNode obj = mapper.createObjectNode();
obj.put("id", 100);
obj.put("name", "Leo");
ArrayNode array = mapper.createArrayNode();
array.add("x").add("y");
obj.set("tags", array);
System.out.println(obj.toPrettyString());输出:
1
2
3
4
5
6
7
8{
"id": 100,
"name": "Leo",
"tags": [
"x",
"y"
]
}
字段操作
ObjectNode
的操作类似于java.util.Map
:1
2
3
4
5
6
7
8
9
10
11
12
13
14ObjectMapper mapper = new ObjectMapper();
ObjectNode root = mapper.createObjectNode();
// 添加字段
root.put("gender", "male");
// 修改字段
root.put("name", "李四");
// 删除字段
root.remove("tags");
// 嵌套修改
((ArrayNode) root.withArray("tags")).add("c");ArrayNode
则类似于java.util.List
:1
2
3
4
5
6
7
8
9
10
11
12ObjectMapper mapper = new ObjectMapper();
ArrayNode root = mapper.createArrayNode();
// 添加元素
root.add("apple");
root.add("banana");
// 修改元素
root.set(1, "orange");
// 删除元素
root.remove(0);
与对象的转换
JsonNode
转换为 Java 对象:1
2
3
4ObjectMapper mapper = new ObjectMapper();
String json = "{}";
JsonNode jsonNode = mapper.readTree(json);
User user = mapper.treeToValue(jsonNode, User.class);带泛型:
1
2
3
4ObjectMapper mapper = new ObjectMapper();
String json = "[]";
JsonNode jsonNode = mapper.readTree(json);
List<User> users = mapper.convertValue(jsonNode, new TypeReference<List<User>>() {});Java 对象转换为
JsonNode
:1
2
3
4
5
6
7ObjectMapper mapper = new ObjectMapper();
User user = new User();
JsonNode jsonNode = mapper.valueToTree(user);
ObjectNode objectNode = mapper.valueToTree(user);
ArrayNode arrayNode = mapper.valueToTree(user);
输出
调用
toString
或toPrettyString
方法可以将JsonNode
对象输出为 JSON 字符串:1
2
3
4
5
6
7
8
9
10
11
12ObjectMapper mapper = new ObjectMapper();
ObjectNode objectNode = mapper.createObjectNode();
objectNode.put("id", 1);
objectNode.put("name", "张三");
// 普通输出
String json = objectNode.toString();
System.out.println(json);
// 美化输出
String prettyJson = objectNode.toPrettyString();
System.out.println(prettyJson); //输出:
1
2
3
4
5
6{"id":1,"name":"张三"}
{
"id" : 1,
"name" : "张三"
}
高级数据绑定
创建
Jackson 的 ObjectMapper
是核心类,它负责所有的 JSON 读写操作。创建方式主要有两种:
使用默认构造方法创建:
1
ObjectMapper objectMapper = new ObjectMapper();
使用 Builder 构建:
1
2
3
4ObjectMapper objectMapper2 = JsonMapper.builder()
.enable(null) // 可以在这里配置一些选项
.disable(null) // 可以在这里配置一些选项
.build();
配置
Jackson 通过以下几组配置类来管理功能开关:
SerializationFeature(序列化特性)
INDENT_OUTPUT
:美化输出,添加缩进与换行,方便调试和阅读。FAIL_ON_EMPTY_BEANS
:序列化空对象(无getter
方法)是否抛出异常,默认开启(true),实际建议关闭。WRITE_DATES_AS_TIMESTAMPS
:默认将Date
和LocalDateTime
序列化为时间戳,建议关闭,转为字符串格式。WRITE_ENUMS_USING_TO_STRING
:启用后将枚举以toString()
方式输出,默认使用name()
。WRITE_ENUMS_USING_INDEX
:将枚举以索引值(ordinal)输出,一般不建议使用。ORDER_MAP_ENTRIES_BY_KEYS
:按照 key 排序序列化Map
,提升 JSON 稳定性,建议开启。WRITE_NULL_MAP_VALUES
:Map 中 value 为 null 是否也序列化,默认 true;可关闭以减少冗余字段。CLOSE_CLOSEABLE
:如果对象实现Closeable
接口,序列化后是否自动关闭资源。FLUSH_AFTER_WRITE_VALUE
:每次写完 JSON 是否自动刷新输出流,默认 true,通常不用管。WRAP_ROOT_VALUE
:输出时是否包裹根元素,需要配合@JsonRootName
使用。EAGER_SERIALIZER_FETCH
:是否预先构建所有序列化器,适合性能敏感场景。USE_EQUALITY_FOR_OBJECT_ID
:序列化中是否通过对象的equals()
判断是否重复引用(与 ObjectId 机制相关)。WRITE_SELF_REFERENCES_AS_NULL
:遇到循环引用时是否写为 null(替代 ObjectId)WRITE_SINGLE_ELEM_ARRAYS_UNWRAPPED
:单元素数组是否去掉[]
包裹,如:开启后 [123] 将会输出为 123。WRITE_CHAR_ARRAYS_AS_JSON_ARRAYS
:字符数组对象char[]
默认输出为字符串,启用后则按字符数组输出,如:[ ‘a’, ‘b’ ]。WRITE_DATE_KEYS_AS_TIMESTAMPS
:用作 Map key 的Date
对象是否写为时间戳(建议关闭)。WRITE_DATES_WITH_ZONE_ID
:启用后将ZonedDateTime
写入完整时区(默认关闭)。WRITE_BIGDECIMAL_AS_PLAIN
:BigDecimal
是否用toPlainString()
(避免科学计数法)。WRITE_DATE_TIMESTAMPS_AS_NANOSECONDS
:默认使用毫秒;启用后转为纳秒精度。一般只在Instant
类中有意义。
DeserializationFeature(反序列化特性)
FAIL_ON_UNKNOWN_PROPERTIES
:JSON 中存在未知字段时是否抛出异常。默认开启(true),建议关闭。示例:mapper.disable(…)。ACCEPT_EMPTY_STRING_AS_NULL_OBJECT
:JSON 中字段值为 “” 时,是否将其视为 null。默认关闭。实际场景如用户输入了空字符串。READ_UNKNOWN_ENUM_VALUES_AS_NULL
:枚举字段如果值不在定义范围内,是否转为 null。默认关闭,建议开启提高兼容性。FAIL_ON_NULL_FOR_PRIMITIVES
:JSON 为 null 时字段是基本类型(如int
),是否抛异常。默认开启。建议明确场景后开启/关闭。FAIL_ON_NUMBERS_FOR_ENUMS
:默认允许枚举用索引值反序列化,启用后将抛出异常(建议开启以防止隐式错误)。USE_BIG_DECIMAL_FOR_FLOATS
:将浮点数反序列化为BigDecimal
,避免精度丢失。金融等高精度场景可启用。USE_BIG_INTEGER_FOR_INTS
:将整型反序列化为BigInteger
,避免溢出。FAIL_ON_READING_DUP_TREE_KEY
:反序列化JsonNode
时,如果同一对象有重复 key,是否抛异常。默认关闭。FAIL_ON_TRAILING_TOKENS
:JSON 反序列化后,如果后面还有多余内容,是否报错。默认关闭。ACCEPT_SINGLE_VALUE_AS_ARRAY
:支持用单个值反序列化为数组。如 1 可反序列化为 [1]。UNWRAP_SINGLE_VALUE_ARRAYS
:支持用单元素数组反序列化为对象。如 [{“a”:1}] 反序列化为单个对象。ACCEPT_EMPTY_ARRAY_AS_NULL_OBJECT
:空数组是否反序列化为 null。ADJUST_DATES_TO_CONTEXT_TIME_ZONE
:日期反序列化时是否调整为上下文时区。FAIL_ON_INVALID_SUBTYPE
:多态反序列化时找不到子类型是否报错。EAGER_DESERIALIZER_FETCH
:启动时是否提前构建所有反序列化器。提升性能但略耗内存。UNWRAP_ROOT_VALUE
:JSON 包裹了根节点(配合@JsonRootName
)时是否自动解包。
MapperFeature(全局映射特性)
AUTO_DETECT_FIELDS
:是否自动检测类中的字段(无getter
/setter
也会被序列化)。默认开启。常配合注解精细控制时禁用。AUTO_DETECT_GETTERS
:自动识别getXxx()
方法。默认开启,禁用后需通过@JsonProperty
注解指定。AUTO_DETECT_IS_GETTERS
:自动识别isXxx()
布尔类型的 getter 方法。AUTO_DETECT_SETTERS
:自动识别setXxx()
方法。DEFAULT_VIEW_INCLUSION
:使用@JsonView
时是否包含未标注字段。默认 true。若希望严格视图隔离,应设为 false。ACCEPT_CASE_INSENSITIVE_PROPERTIES
:属性名大小写是否不敏感。默认 false,开启后 JSON 中 “Name” 可以映射到 name。USE_ANNOTATIONS
:是否启用注解识别(如@JsonProperty
)。建议开启(默认)。CAN_OVERRIDE_ACCESS_MODIFIERS
:是否允许通过反射修改字段可见性(如 private → public)。INFER_PROPERTY_MUTATORS
:是否根据 getter 推断 setter。一般保持默认即可。REQUIRE_SETTERS_FOR_GETTERS
:是否必须存在 setter,才能序列化 getter。ALLOW_FINAL_FIELDS_AS_MUTATORS
:final 字段是否作为反序列化目标。USE_STATIC_TYPING
:是否强制使用静态类型(一般用于性能优化)。INFER_CREATOR_FROM_CONSTRUCTOR_PROPERTIES
:启用后会根据构造方法参数名自动推断@JsonCreator
。PROPAGATE_TRANSIENT_MARKER
:标准 transient 关键字是否影响序列化(默认会被忽略)。APPLY_DEFAULT_VALUES
:序列化时是否写入字段默认值。BLOCK_UNSAFE_POLYMORPHIC_BASE_TYPES
:防止某些类型被不安全反序列化(安全相关,默认 true)。
JsonInclude(字段包含策略)
ALWAYS
:默认,始终包含所有字段(即使是 null、空字符串、空集合),这是默认行为。NON_NULL
:仅包含 非 null 字段。常用来压缩 JSON 输出、避免多余的字段。NON_EMPTY
:仅包含 非空字段,即字段不为 null、不为空集合、不为空字符串。NON_DEFAULT
:仅包含 非默认值字段。对于 int=0、boolean=false、对象为默认构造值的字段会被排除。USE_DEFAULTS
:仅在@JsonInclude
中使用,表示使用 Jackson 全局默认配置。CUSTOM
:表示使用自定义策略类(配合@JsonInclude(value = CUSTOM, valueFilter = Xxx.class)
)。
推荐配置
1 |
|
序列化 / 反序列化
使用 writeValue
以及 readValue
方法来进行序列化以及反序列化:
1 |
|
泛型处理
Jackson 反序列化泛型集合时由于类型擦除,必须使用 TypeReference
来保留泛型信息。
List/Set:
1 |
|
Map:
1 |
|
嵌套结构:
1 |
|
对象转换
用于 Java 对象之间转换,不走序列化过程,适合对象结构变化时使用。
1 |
|
枚举处理
Jackson 默认使用 name()
来序列化和反序列化,可设置:
- 通过
SerializationFeature.WRITE_ENUMS_USING_TO_STRING
控制序列化的值为toString()
方法输出值。 - 通过
@JsonCreator
从 JSON 字符串中解析出对应的枚举值。
1 |
|
时间日期
对于 java.util.Date
,默认使用会将其输出为时间戳,可通过 Feature
进行配置,让其按指定的时间日期格式以及时区进行序列化和反序列化,如:
1 |
|
或者通过优先级更高的注解:
1 |
|
对于 Java 8 的时间日期,则需要给 ObjectMapper 注册 JavaTimeModule,如:
1 |
|
之后 LocalDate 或 LocalDateTime 配合注解使用:
1 |
|
科学计数法
通过注解 @JsonSerialize(using = ToStringSerializer.class)
可避免 BigDecimal
对象变成科学计数法,同时以字符串的形式输出,避免前端精度丢失(特别是 JavaScript 精度超过 15 位的情况)。
示例:
1 |
|
JSON 字段输出如下:
1 |
|
注解
字段控制
@JsonProperty
作用:
- 指定序列化/反序列化时字段的 JSON 名称(默认使用字段名)。
- 可用于字段或方法(getter/setter)。
- 可用于启用反射不可访问字段(ObjectMapper 对象需配置 MapperFeature.USE_ANNOTATIONS)。
常用参数:
value
:指定序列化/反序列化使用的 JSON 名称(如 “user_id”)。access
:指定访问类型(序列化 -READ_ONLY
、反序列化 -WRITE_ONLY
、都有 -READ_WRITE
)。required
:指定是否必须存在于 JSON 中(用于反序列化校验)。index
:指定序列化时字段的顺序(用于数组格式)。
示例:
1 |
|
@JsonAlias
作用:
- 定义反序列化时字段的别名,支持多个 JSON 字段反序列化为同一个 Java 字段(只影响反序列化)。
- 常用于前后端字段命名不统一、版本兼容。
示例:
1 |
|
效果:
以下 JSON 都可以被反序列化为 User.id
1 |
|
@JsonInclude
作用:
- 设置字段在序列化为 JSON 时的输出策略。
常用值:
ALWAYS
:默认,始终包含所有字段(即使是 null、空字符串、空集合),这是默认行为。NON_NULL
:仅包含 非 null 字段。常用来压缩 JSON 输出、避免多余的字段。NON_EMPTY
:仅包含 非空字段,即字段不为 null、不为空集合、不为空字符串。NON_DEFAULT
:仅包含 非默认值字段。对于 int=0、boolean=false、对象为默认构造值的字段会被排除。USE_DEFAULTS
:仅在 @JsonInclude 中使用,表示使用 Jackson 全局默认配置。CUSTOM
:表示使用自定义策略类(配合 @JsonInclude(value = CUSTOM, valueFilter = Xxx.class))。
1 |
|
@JsonIgnore
作用:
- 完全忽略该字段的序列化、反序列化。
- 通常用于敏感字段(如密码、内部字段等)
示例:
1 |
|
效果:
1 |
|
格式化控制
@JsonFormat
作用:
- 控制序列化和反序列化时的格式,支持时间、数字、布尔值等字段。
- 可指定格式字符串、时区、形状(如转字符串、对象等)。
常用字段:
pattern
:格式字符串,如"yyyy-MM-dd HH:mm:ss"
。timezone
:时区,如"GMT+8"
或"Asia/Shanghai"
。shape
:序列化形状,常用如Shape.STRING
、Shape.NUMBER
、Shape.OBJECT
等。
示例:
1 |
|
效果:
1 |
|
@JsonSerialize
作用:
- 序列化器,控制字段的序列化行为,用于特殊字段格式的输出。
- 常用于:
BigDecimal
精度、枚举转中文、敏感字段脱敏等。
示例:
1 |
|
将 BigDecimal
以字符串形式输出,避免前端 JS 精度丢失。
拓展:
自定义手机号码序列化器:
1 |
|
使用:
1 |
|
1 |
|
@JsonDeserialize
作用:
- 自定义反序列化器,控制反序列化逻辑,常用于复杂类型、枚举、特殊格式处理等。
示例:
自定义枚举反序列化器:
1 |
|
使用:
1 |
|
类型信息与多态
@JsonTypeInfo
作用:
- 告诉 Jackson 在序列化和反序列化时,需要保留或识别子类的类型信息。
- 用于处理 多态(接口或抽象类)反序列化。
常用属性:
use
:类型识别机制,指定 Jackson 用什么方式标识类型。Id.CLASS
:使用全限定类名(com.example.Dog
),安全性差,一般用于测试。Id.MINIMAL_CLASS
:使用类名(相对路径,前缀省略)Dog
,仅在模块内部使用。Id.NAME
:使用名称标识,由@JsonSubTypes
提供映射,推荐用于接口/继承体系。Id.NONE
:不使用类型信息,不常用。Id.CUSTOM
:自定义类型识别方式,需实现TypeIdResolver
。
include
:指定类型信息在 JSON 中的“插入位置”。As.PROPERTY
:作为对象字段存在,如{ "type": "dog", "name": "旺财" }
。As.WRAPPER_OBJECT
:用类型名包裹对象,如{ "dog": { "name": "旺财" } }
。As.WRAPPER_ARRAY
:包裹成数组:[type, object]
,如[ "dog", { "name": "旺财" } ]
。As.EXTERNAL_PROPERTY
:类型字段放在外层,与对象并列,如{ "type": "dog", "animal": { "name": "旺财" } }
。As.EXISTING_PROPERTY
:类型信息存放在对象已有字段上,高级用法。
property
:定义 JSON 中用于表示“类型”的字段名,默认是"@class"
或"type"
,可自定义。visible
:是否将property
字段(即"type"
)作为 POJO 的字段值注入,默认 false。defaultImpl
:当 JSON 中缺少类型字段(property
)时,反序列化使用的默认类。
@JsonSubTypes
作用:
- 使用在类上,明确标注有哪些子类。
- 与
@JsonTypeInfo
配套使用,指定子类与标识符的映射关系。 - 每个子类用一个
@JsonSubTypes.Type
表示。
示例:
1 |
|
以下 JSON 可被反序列为 Dog
类:
1 |
|
构造函数绑定
@JsonCreator
作用:
- 告诉 Jackson:反序列化对象时该构造函数或静态工厂方法可以用来创建对象。
- 常用于:不可变对象(无 setter)、字段为
final
、构造参数较复杂时。
示例一:
构造函数 + @JsonProperty
1 |
|
示例二:
静态工厂方法
1 |
|
动态属性处理
@JsonAnySetter
作用:
- 动态接收 JSON 中未知字段。
- 当 JSON 中出现 POJO 未定义的字段时,用该方法接收这些字段。
- 常用于:键值对类型对象、不确定字段结构场景。
示例:
1 |
|
JSON 输入:
1 |
|
结果:
1 |
|
name
→ 映射到字段。id
/age
→ 存储哈希表 extra 中。
@JsonAnyGetter
作用:
- 与
@JsonAnySetter
相对,用于将 Map 中的数据当作字段动态序列化输出。
示例:
1 |
|
设置数据:
1 |
|
输出为:
1 |
|
高级特性
Mix-in
定义
Mix-in 是 Jackson 提供的一种“影子注解”机制,可以让你在不修改原始类源码的情况下,给类“动态添加 Jackson 注解”。
使用场景
- 第三方类无法添加注解(如 JDK 自带类、外部依赖类);
- 你想将 Jackson 注解与业务类解耦(领域模型 vs 展示模型);
- 多个上下文下使用同一类,但需要不同的序列化逻辑。
基本用法
假设目标类为第三方提供的类、且无法修改
1
2
3
4public class OnlyReadClass {
private Long id;
private String name;
}编写一个影子类(Mix-in 类)
1
2
3
4
5
6
7
8
9
10
11
12
13@Data
@NoArgsConstructor
@AllArgsConstructor
@Accessors(chain = true)
public class OnlyReadClassMixIn {
@JsonProperty("or_id")
private Long id;
@JsonProperty("or_name")
private String name;
}注意影子类的属性应于源类的属性名称相同。
使用:在进行序列化或反序列化前,将目标类与影子类进行绑定
1
mapper.addMixIn(OnlyReadClass.class, OnlyReadClassMixIn.class);
之后影子类的序列化行为就等价于加上了影子类中的注解
1
2
3OnlyReadClass onlyReadClass = new OnlyReadClass().setId(1L).setName("只读类");
String json = mapper.writeValueAsString(onlyReadClass);
// json为:{"or_id":1,"or_name":"只读类"}1
2
3String fromJson = "{\"or_id\":1,\"or_name\":\"只读类\"}";
OnlyReadClass orClass = mapper.readValue(fromJson, OnlyReadClass.class);
// orClass为:OnlyReadClass(id=1, name=只读类)
自定义序列化器
序列化器
场景:
- 特殊格式输出,如手机号脱敏、金额格式化。
- 将对象转为特定 JSON 字符串。
示例:
继承 JsonSerializer
类,重写序列化方法 serialize
:
1 |
|
使用:
在类属性上通过 @JsonSerialize
指定该属性的序列化器:
1 |
|
反序列化器
场景:
- 将字符串转为枚举。
- 将特殊格式(如前端传的 yyyyMMdd)转为 Java 时间对象。
示例:
继承 JsonDeserializer
类,重写反序列化方法 deserialize
:
1 |
|
使用:
在类属性上通过 @JsonDeserialize
指定该属性的反序列化器:
1 |
|
全局注册
可通过 SimpleModule
注册自定义的反序列化起到全局的 ObjectMapper
:
1 |
|
注意事项
要点 | 说明 |
---|---|
注解优先级高 | 如果字段上同时注册了 @JsonSerialize ,则会覆盖全局注册。 |
建议封装公共序列化器 | 如 ToStringSerializer 、BigDecimalKeep2Serializer 等。 |
可结合 JsonComponent 自动注册 |
Spring Boot 可使用 @JsonComponent 自动扫描注册。 |
模块机制
机制
模块是 Jackson 提供的一种插件机制,可以注册自定义或第三方提供的:
- 自定义序列化器 / 反序列化器
- 类型处理器
- 字段命名策略
- Bean 修饰器(如脱敏、默认值)
- 子类型注册
- 注解解释器
核心类结构
com.fasterxml.jackson.databind.Module
:抽象类,所有模块的顶层父类。SimpleModule
:最常用模块类,支持注册序列化器/反序列化器等。JavaTimeModule
:官方提供的支持 Java8 日期时间类型的模块。AfterburnerModule
:动态生成字节码提升性能。ParameterNamesModule
:支持构造器参数名推断。Module.SetupContext
:模块上下文,用于向 Jackson 注册类型处理器等。
优点
全局统一规则,可以为所有
BigDecimal
、手机号、日期等设定统一策略。免注解、免侵入,不用修改实体类源码,适合第三方类或解耦业务。
可组合、可扩展,支持字段名、字段注解、自定义标记等条件化逻辑。
使用方式
通过 registerModule
方法为 ObjectMapper
注册模块,如 Java 8 日期支持模块:
1 |
|
其他常见模块:
jackson-module-kotlin
:Kotlin 支持。jackson-module-parameter-names
:支持构造函数参数名识别(配合@JsonCreator
)。jackson-datatype-jdk8
:Optional
、Stream
支持。jackson-module-afterburner
:性能加速器(生成动态字节码)。
自定义模块
以加密手机号场景为例:
创建加密手机号类:
1
2
3
4
5
6
7
8
9@Data
@NoArgsConstructor
@AllArgsConstructor
@Accessors(chain = true)
public class EncryptedPhone {
private String originalNum;
}自定义加密手机号的序列化器
EncryptedPhoneSerializer
,对手机号字段进行处理:1
2
3
4
5
6
7
8
9
10
11public class EncryptedPhoneSerializer extends JsonSerializer<EncryptedPhone> {
@Override
public void serialize(EncryptedPhone value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
if (value == null || value.getOriginalNum() == null) {
gen.writeNull();
return;
}
gen.writeString(value.getOriginalNum().replaceAll("(\\d{3})(\\d{4})(\\d{4})", "$1****$3"));
}
}自定义一个序列化修改器,为
EncryptedPhone
添加特定的序列化器1
2
3
4
5
6
7
8
9
10
11
12
13
14
15public class EncryptedPhoneModifier extends BeanSerializerModifier {
@Override
public List<BeanPropertyWriter> changeProperties(SerializationConfig config,
BeanDescription beanDesc,
List<BeanPropertyWriter> beanProperties) {
for (BeanPropertyWriter writer : beanProperties) {
if (writer.getType().getRawClass() == EncryptedPhone.class) {
EncryptedPhoneSerializer encryptedPhoneSerializer = new EncryptedPhoneSerializer();
writer.assignSerializer((JsonSerializer) encryptedPhoneSerializer);
}
}
return beanProperties;
}
}使用时将序列化修改器注册到模块中:
1
2
3
4
5
6
7
8
9// 注册
ObjectMapper mapper = new ObjectMapper();
SimpleModule simpleModule = new SimpleModule();
simpleModule.setSerializerModifier(new EncryptedPhoneModifier());
mapper.registerModule(simpleModule);
// 序列化
User user = new User().setPhone(new EncryptedPhone("13545678901"));
System.out.println(mapper.writeValueAsString(user));运行程序,控制台输出加密手机号:
1
{"phone": "135****8901"}
视图与过滤器
@JsonView
作用:
- 控制字段在不同场景下是否参与序列化(类似权限视图、版本控制)。
- 一般配合
ObjectMapper.writerWithView()
使用。 - 默认 Jackson 序列化时不会使用视图,需显式调用。
示例:
定义视图类:
1
2
3
4
5
6
7
8
9public class Views {
public static class Public {
}
public static class Internal extends Public {
}
}为属性标注视图:
1
2
3
4
5@JsonView(Views.Public.class)
public String publicField;
@JsonView(Views.Internal.class)
public String internalField;在
ObjectMapper
写出 JSON 前通过writerWithView()
方法指定需要输出的特定视图:1
2
3
4
5ObjectMapper mapper = getCommonObjectMapper();
User user = new User().setPublicField("p").setInternalField("i");
String json = mapper.writerWithView(Views.Public.class)
.writeValueAsString(user);
System.out.println(json);输出:
1
2
3{
"publicField" : "p"
}
FilterProvider/@JsonFilter
作用:
- Jackson 过滤器机制允许在运行时动态决定输出哪些字段。
- 通常用于字段级权限控制、字段白名单/黑名单、属性排除等。
- 比
@JsonView
更动态、更灵活(但也更复杂)。
示例:
给实体类加上
@JsonFilter
注解:1
2
3
4
5@JsonFilter("userFilter")
public class User {
public String publicField;
public String internalField;
}定义过滤器规则:
1
2SimpleBeanPropertyFilter filter = SimpleBeanPropertyFilter.filterOutAllExcept("publicField");
SimpleFilterProvider userFilter = new SimpleFilterProvider().addFilter("userFilter", filter);绑定过滤器到
ObjectMapper
写出 JSON:1
2
3User user = new User().setPublicField("p").setInternalField("i");
String json = mapper.writer(userFilter).writeValueAsString(user);
System.out.println(json);输出:
1
2
3{
"publicField" : "p"
}
对比
功能项 | @JsonView | @JsonFilter + FilterProvider |
---|---|---|
控制粒度 | 静态字段注解 | 运行时动态 |
使用方式 | 依赖注解 + writerWithView |
依赖注解 + writer(filters) |
灵活性 | 中 | 高 |
推荐场景 | 多角色/视图切换 | 字段权限、白名单/黑名单、动态字段输出 |
Spring 支持 | 完全支持(可直接 @JsonView 返回) |
不直接支持,需手动控制输出 |
用法建议
场景 | 建议用法 |
---|---|
管理后台接口和前台接口输出字段不同 | @JsonView |
某些字段要根据权限动态控制是否输出 | @JsonFilter |
想做统一的字段输出白名单机制 | @JsonFilter |
仅部分模型需要简单视图区分 | @JsonView 更易用 |
复杂系统字段访问控制 | 搭配 Spring Security + @JsonFilter 实现 |
循环引用解决
父子结构中双向引用
1 |
|
赋值:
1 |
|
序列化会导致无线递归:
1 |
|
双向关系
利用 @JsonManagedReference
/@JsonBackReference
解决。
作用:
- 标记父子关系的一方为“主动方”(序列化),另一方为“反向方”(不序列化);
- 非对称:一个写出,另一个不写。
示例:
1 |
|
当序列化 Parent 时,能正确输出 child:
1 |
|
当序列化 Child 时,会忽略 parent:
1 |
|
基于ID追踪
通过 @JsonIdentityInfo
实现基于 ID 追踪对象引用。
作用:
- 给对象加一个 ID(通常是
@id
),Jackson 会在后续引用中使用这个 ID 来代替完整对象,从而避免循环引用。 - 可同时支持序列化和反序列化。
示例:
1 |
|
赋值:
1 |
|
序列化输出:
1 |
|
常见 ID 生成器:
-
ObjectIdGenerators.PropertyGenerator
:按属性(如 id 字段)。 -
ObjectIdGenerators.UUIDGenerator
:自动 UUID。 -
ObjectIdGenerators.IntSequenceGenerator
:递增整数。
流式处理
Streaming API 是 Jackson 最底层的处理方式,基于两个核心类:
JsonParser
(读取 JSON)JsonGenerator
(写入 JSON)
它们是基于 token 流的模型(字段、值、开始/结束对象/数组等都作为独立 token 处理)。
特点:
- 内存占用极低:不构造对象,不构造树结构。
- 性能最高:适合大文件、流式网络。
- 编程复杂度高:需要手动管理 token 状态机。
读取示例
1 |
|
输出:
1 |
|
写入示例
1 |
|
输出:
1 |
|
非阻塞解析
从 Jackson 2.9+ 开始,引入非阻塞 JSON 解析器,可以处理异步数据流(如 WebSocket、Netty、NIO ByteBuffer)。
特点
- 增量处理:支持一部分一部分地喂数据。
- IO 解耦:非阻塞 IO 配合良好(如 Netty)。
- 低延迟:可边接收边解析,无需等整个 JSON 到达。
示例
1 |
|
注意事项
feedInput(byte[], offset, len)
:喂数据之前,Jackson 必须消费完前一批数据。- 使用
parser.endOfInput()
:告诉解析器“没有更多了”,必要。 - 不要重复喂未消费完的数据:否则会报错 Input start/end mismatch。
Spring Boot集成
默认配置入口
Spring Boot 自动装配 Jackson 的核心类是:
1 |
|
它会自动注册以下 Bean:
ObjectMapper
:全局唯一,推荐注入使用Jackson2ObjectMapperBuilder
:构造器工厂,允许定制化配置- 配合
application.yml
中的spring.jackson.*
属性自动生效
配置文件配置
1 |
|
自定义ObjectMapper
主要配置点:
- 忽略空值及空对象,不会因空对象报错。
- 接收未知字段而不报错。
- 枚举序列化方法重写,未知枚举值时反序列化为 null。
- 关闭时间戳、指定时区。
- Java 8 支持:注册
JavaTimeModule
、Jdk8Module
支持LocalDateTime
、Optional
等。 - 数值精度保护:全局将
Long
/long
/BigDecimal
序列化为字符串 避免 JavaScript 精度丢失。 Map
排序保证输出结构稳定,可用于签名校验。char[]
输出为数组。- 保持默认的自动探测(字段/Getter/Setter)+ 开启注解 兼容绝大多数 POJO,无需每个字段都打注解。
最终配置类如下:
1 |
|
工具类封装
1 |
|
总结
Jackson 不仅是一个强大的 JSON 序列化工具,更是现代 Java 项目中不可或缺的基础组件。通过合理配置 ObjectMapper
、掌握核心注解、灵活使用模块机制,开发者可以实现对各种复杂数据结构、时间格式、枚举、多态等场景的精准控制。无论是处理简单对象,还是构建可拓展的框架工具类,Jackson 都提供了足够的能力与自由度。本文的讲解能协助开发者从底层原理到高级技巧全面掌握 Jackson,使 JSON 数据交互更安全、高效、优雅。
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!