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)

  • 使用:JsonParserJsonGenerator

  • 优点:性能最高,占用内存最小。

  • 缺点:编程复杂,手动处理 token

  • 示例:

    1
    2
    3
    4
    5
    6
    JsonFactory factory = new JsonFactory();
    JsonParser parser = factory.createParser(jsonStr);
    while (!parser.isClosed()) {
    JsonToken token = parser.nextToken();
    // 自行处理键值
    }

Tree Model(中级树形模型)

  • 使用:JsonNodeObjectMapper.readTree()writeTree()

  • 优点:动态结构处理方便,类似 DOM。

  • 缺点:性能中等,适合不确定结构的 JSON。

  • 示例:

    1
    2
    JsonNode rootNode = objectMapper.readTree(jsonStr);
    String name = rootNode.get("name").asText();

Data Binding(高级数据绑定)

  • 使用:ObjectMapper.readValue()writeValueAsString()

  • 优点:开发效率高,自动映射 POJO,最常用方式。

  • 缺点:对于复杂结构需要借助注解、自定义序列化处理。

  • 示例:

    1
    2
    User 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

  1. 创建 JsonNode

    1
    2
    3
    4
    String json = "{\"id\":1,\"name\":\"张三\",\"tags\":[\"a\",\"b\"]}";

    ObjectMapper mapper = new ObjectMapper();
    JsonNode root = mapper.readTree(json);
  2. 以指定的数据类型获取字段:

    1
    2
    3
    int id = root.get("id").asInt();            // 1
    String name = root.get("name").asText(); // 张三
    JsonNode tags = root.get("tags"); // ArrayNode
  3. 遍历数组:

    1
    2
    3
    for (JsonNode tagNode : tags) {
    System.out.println(tagNode.asText()); // a, b
    }
  4. 安全访问(避免空指针):

    1
    String nickname = root.path("nickname").asText("默认昵称");  // 不存在时返回默认值

构建 JsonNode

  1. 手动创建 JSON 对象

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    ObjectMapper 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());
  2. 输出:

    1
    2
    3
    4
    5
    6
    7
    8
    {
    "id": 100,
    "name": "Leo",
    "tags": [
    "x",
    "y"
    ]
    }

字段操作

  1. ObjectNode 的操作类似于 java.util.Map

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    ObjectMapper mapper = new ObjectMapper();
    ObjectNode root = mapper.createObjectNode();

    // 添加字段
    root.put("gender", "male");

    // 修改字段
    root.put("name", "李四");

    // 删除字段
    root.remove("tags");

    // 嵌套修改
    ((ArrayNode) root.withArray("tags")).add("c");
  2. ArrayNode 则类似于 java.util.List

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    ObjectMapper mapper = new ObjectMapper();
    ArrayNode root = mapper.createArrayNode();

    // 添加元素
    root.add("apple");
    root.add("banana");

    // 修改元素
    root.set(1, "orange");

    // 删除元素
    root.remove(0);

与对象的转换

  1. JsonNode 转换为 Java 对象:

    1
    2
    3
    4
    ObjectMapper mapper = new ObjectMapper();
    String json = "{}";
    JsonNode jsonNode = mapper.readTree(json);
    User user = mapper.treeToValue(jsonNode, User.class);
  2. 带泛型:

    1
    2
    3
    4
    ObjectMapper mapper = new ObjectMapper();
    String json = "[]";
    JsonNode jsonNode = mapper.readTree(json);
    List<User> users = mapper.convertValue(jsonNode, new TypeReference<List<User>>() {});
  3. Java 对象转换为 JsonNode

    1
    2
    3
    4
    5
    6
    7
    ObjectMapper mapper = new ObjectMapper();
    User user = new User();

    JsonNode jsonNode = mapper.valueToTree(user);

    ObjectNode objectNode = mapper.valueToTree(user);
    ArrayNode arrayNode = mapper.valueToTree(user);

输出

  1. 调用 toStringtoPrettyString 方法可以将 JsonNode 对象输出为 JSON 字符串:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    ObjectMapper 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); //
  2. 输出:

    1
    2
    3
    4
    5
    6
    {"id":1,"name":"张三"}

    {
    "id" : 1,
    "name" : "张三"
    }

高级数据绑定

创建

Jackson 的 ObjectMapper 是核心类,它负责所有的 JSON 读写操作。创建方式主要有两种:

  1. 使用默认构造方法创建:

    1
    ObjectMapper objectMapper = new ObjectMapper();
  2. 使用 Builder 构建:

    1
    2
    3
    4
    ObjectMapper objectMapper2 = JsonMapper.builder()
    .enable(null) // 可以在这里配置一些选项
    .disable(null) // 可以在这里配置一些选项
    .build();

配置

Jackson 通过以下几组配置类来管理功能开关:

SerializationFeature(序列化特性)

  • INDENT_OUTPUT:美化输出,添加缩进与换行,方便调试和阅读。
  • FAIL_ON_EMPTY_BEANS:序列化空对象(无 getter 方法)是否抛出异常,默认开启(true),实际建议关闭。
  • WRITE_DATES_AS_TIMESTAMPS:默认将 DateLocalDateTime 序列化为时间戳,建议关闭,转为字符串格式。
  • 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_PLAINBigDecimal 是否用 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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
ObjectMapper mapper = new ObjectMapper();

// 配置序列化特征
// 美化输出(开发环境下使用)
mapper.enable(SerializationFeature.INDENT_OUTPUT);
// 禁止序列化空对象(默认是抛出异常)
mapper.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS);
// 禁止将日期序列化为时间戳(默认是时间戳)
mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
// 按照键的顺序序列化Map(默认是无序)
mapper.enable(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS);
// 枚举序列化为字符串(默认是枚举的toString方法)
mapper.enable(SerializationFeature.WRITE_ENUMS_USING_TO_STRING);
// char数组作为JSON数组序列化(默认是字符串)
mapper.enable(SerializationFeature.WRITE_CHAR_ARRAYS_AS_JSON_ARRAYS);

// 配置反序列化特征
// 禁止反序列化未知属性(默认是抛出异常)
mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
// 允许读取未知的枚举值为null(默认是抛出异常)
mapper.enable(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_AS_NULL);

// 配置全局映射特性:如果禁用了自动探测,必须在字段或对应的setter/getter上加@JsonProperty才能正常序列化或反序列化。
// 禁止自动检测字段(默认是自动检测)
// mapper.disable(MapperFeature.AUTO_DETECT_FIELDS);
// 禁止自动检测getter方法(默认是自动检测)
// mapper.disable(MapperFeature.AUTO_DETECT_GETTERS);
// 禁止自动检测is方法(默认是自动检测)
// mapper.disable(MapperFeature.AUTO_DETECT_IS_GETTERS);
// 禁止自动检测setter方法(默认是自动检测)
// mapper.disable(MapperFeature.AUTO_DETECT_SETTERS);
// 启用注解(默认是启用)
mapper.enable(MapperFeature.USE_ANNOTATIONS);

// 配置字段包含策略
// 设置序列化时忽略null值(默认是序列化null值)
mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);

序列化 / 反序列化

使用 writeValue 以及 readValue 方法来进行序列化以及反序列化:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// Java 对象 → JSON 字符串
String json = mapper.writeValueAsString(obj);

// JSON 字符串 → Java 对象
User user = mapper.readValue(json, User.class);

// JSON 字符串 → JsonNode(树模型)
JsonNode node = mapper.readTree(json);

// JSON 写入文件
mapper.writeValue(new File("user.json"), obj);

// 从文件反序列化
User user = mapper.readValue(new File("user.json"), User.class);

// 写入输出流
mapper.writeValue(OutputStream out, Object value);

// 从输入流读取
mapper.readValue(InputStream in, Class<T> valueType);

泛型处理

Jackson 反序列化泛型集合时由于类型擦除,必须使用 TypeReference 来保留泛型信息。

List/Set

1
2
String json = "[{\"name\":\"张三\"},{\"name\":\"李四\"}]";
List<User> list = mapper.readValue(json, new TypeReference<List<User>>() {});

Map

1
2
String json = "{\"name\":\"张三\",\"age\":18}";
Map<String, Object> map = mapper.readValue(json, new TypeReference<Map<String, Object>>() {});

嵌套结构

1
Map<String, List<User>> map = mapper.readValue(json, new TypeReference<Map<String, List<User>>>() {});

对象转换

用于 Java 对象之间转换,不走序列化过程,适合对象结构变化时使用。

1
mapper.convertValue(source, new TypeReference<目标类型>() {});

枚举处理

Jackson 默认使用 name() 来序列化和反序列化,可设置:

  • 通过 SerializationFeature.WRITE_ENUMS_USING_TO_STRING 控制序列化的值为 toString() 方法输出值。
  • 通过 @JsonCreator 从 JSON 字符串中解析出对应的枚举值。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
@Getter
@AllArgsConstructor
public enum Gender {
MALE("male", "男"),
FEMALE("female", "女"),
UNKNOWN("unknown", "未知");

private final String value;
private final String description;

// ObjectMapper添加WRITE_ENUMS_USING_TO_STRING特性后,会使用toString方法序列化枚举值
@Override
public String toString() {
return value;
}

// 反序列化时使用的工厂方法
@JsonCreator
public static Gender fromValue(String value) {
for (Gender gender : values()) {
if (gender.value.equals(value)) {
return gender;
}
}
return UNKNOWN; // 如果没有匹配的值,返回默认值
}
}

时间日期

对于 java.util.Date,默认使用会将其输出为时间戳,可通过 Feature 进行配置,让其按指定的时间日期格式以及时区进行序列化和反序列化,如:

1
2
3
mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
mapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
mapper.setTimeZone(SimpleTimeZone.getTimeZone("Asia/Shanghai"));

或者通过优先级更高的注解:

1
2
3
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date birthday;

对于 Java 8 的时间日期,则需要给 ObjectMapper 注册 JavaTimeModule,如:

1
mapper.registerModule(new JavaTimeModule());

之后 LocalDate 或 LocalDateTime 配合注解使用:

1
2
3
4
5
6
7
@JsonFormat(pattern = "yyyy-MM-dd")
@DateTimeFormat(pattern = "yyyy-MM-dd")
private LocalDate birthday;

@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime createTime;

科学计数法

通过注解 @JsonSerialize(using = ToStringSerializer.class) 可避免 BigDecimal 对象变成科学计数法,同时以字符串的形式输出,避免前端精度丢失(特别是 JavaScript 精度超过 15 位的情况)。

示例:

1
2
3
4
private BigDecimal unsafeAmount;

@JsonSerialize(using = ToStringSerializer.class)
private BigDecimal safeAmount;

JSON 字段输出如下:

1
2
3
4
{
"unsafeAmount" : 123456789012345678905555555555.123456,
"safeAmount" : "123456789012345678905555555555.123456",
}

注解

字段控制

@JsonProperty

作用

  • 指定序列化/反序列化时字段的 JSON 名称(默认使用字段名)。
  • 可用于字段或方法(getter/setter)。
  • 可用于启用反射不可访问字段(ObjectMapper 对象需配置 MapperFeature.USE_ANNOTATIONS)。

常用参数

  • value:指定序列化/反序列化使用的 JSON 名称(如 “user_id”)。
  • access:指定访问类型(序列化 - READ_ONLY、反序列化 - WRITE_ONLY、都有 - READ_WRITE)。
  • required:指定是否必须存在于 JSON 中(用于反序列化校验)。
  • index:指定序列化时字段的顺序(用于数组格式)。

示例

1
2
3
4
5
6
7
public class User {
@JsonProperty("user_id")
private Long id;

@JsonProperty("user_name")
private String name;
}

@JsonAlias

作用

  • 定义反序列化时字段的别名,支持多个 JSON 字段反序列化为同一个 Java 字段(只影响反序列化)。
  • 常用于前后端字段命名不统一、版本兼容。

示例

1
2
3
4
public class User {
@JsonAlias({"id", "userId", "user_id"})
private Long id;
}

效果

以下 JSON 都可以被反序列化为 User.id

1
2
3
{"id": "1"}
{"userId": "1"}
{"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
2
3
4
5
@JsonInclude(JsonInclude.Include.NON_NULL)
public class User {
private Long id;
private String name; // 如果为 null,不输出
}

@JsonIgnore

作用

  • 完全忽略该字段的序列化、反序列化。
  • 通常用于敏感字段(如密码、内部字段等)

示例

1
2
3
4
5
6
public class User {
private Long id;

@JsonIgnore
private String password;
}

效果

1
2
3
4
5
User user = new User().setPassword("123456");
System.out.println(mapper.writeValueAsString(user)); // 输出:{ }

String json = "{\"password\":1}";
System.out.println(mapper.readValue(json, User.class)); // 输出:User(id=null, password=null)

格式化控制

@JsonFormat

作用

  • 控制序列化和反序列化时的格式,支持时间、数字、布尔值等字段。
  • 可指定格式字符串、时区、形状(如转字符串、对象等)。

常用字段

  • pattern:格式字符串,如 "yyyy-MM-dd HH:mm:ss"
  • timezone:时区,如 "GMT+8""Asia/Shanghai"
  • shape:序列化形状,常用如 Shape.STRINGShape.NUMBERShape.OBJECT 等。

示例

1
2
3
4
5
6
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime createTime;

@JsonFormat(shape = JsonFormat.Shape.STRING)
private boolean active;

效果

1
2
3
4
5
User user = new User().setActive(true).setCreateTime(LocalDateTime.now());
System.out.println(mapper.writeValueAsString(user)); // {"active":true, "createTime":"2025-07-13 17:12:17"}

String json = "{\"active\":true, \"createTime\":\"2025-07-13 17:12:17\"}";
System.out.println(mapper.readValue(json, User.class)); // User(active=false, createTime=2025-07-13T17:12:17)

@JsonSerialize

作用

  • 序列化器,控制字段的序列化行为,用于特殊字段格式的输出。
  • 常用于:BigDecimal 精度、枚举转中文、敏感字段脱敏等。

示例

1
2
@JsonSerialize(using = ToStringSerializer.class)
private BigDecimal safeAmount;

BigDecimal 以字符串形式输出,避免前端 JS 精度丢失。

拓展

自定义手机号码序列化器:

1
2
3
4
5
6
public class PhoneNumberSerializer extends JsonSerializer<String> {
@Override
public void serialize(String value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
gen.writeString("***" + value.substring(value.length() - 4));
}
}

使用:

1
2
@JsonSerialize(using = PhoneNumberSerializer.class)
private String phoneNumber;
1
2
User user = new User().setPhoneNumber("12345678901");
System.out.println(mapper.writeValueAsString(user)); // 输出:{"phoneNumber":"123****8901"}

@JsonDeserialize

作用

  • 自定义反序列化器,控制反序列化逻辑,常用于复杂类型、枚举、特殊格式处理等。

示例

自定义枚举反序列化器:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class GenderDeserializer extends JsonDeserializer<User.Gender> {
@Override
public User.Gender deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
String value = p.getText();
if (value == null || value.isEmpty()) {
return User.Gender.UNKNOWN;
}
switch (value) {
case "男":
return User.Gender.MALE;
case "女":
return User.Gender.FEMALE;
default:
return User.Gender.UNKNOWN;
}
}
}

使用:

1
2
@JsonDeserialize(using = GenderDeserializer.class)
private Gender gender;

类型信息与多态

@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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@JsonTypeInfo(
use = JsonTypeInfo.Id.NAME,
include = JsonTypeInfo.As.PROPERTY,
property = "type"
)
@JsonSubTypes({
@JsonSubTypes.Type(value = Animal.Dog.class, name = "dog"),
@JsonSubTypes.Type(value = Animal.Cat.class, name = "cat")
})
public class Animal {

public static class Dog extends Animal {
public int boneCount;
}

public static class Cat extends Animal {
public boolean lazy;
}

}

以下 JSON 可被反序列为 Dog 类:

1
2
3
4
5
{
"type": "dog",
"name": "旺财",
"boneCount": 5
}

构造函数绑定

@JsonCreator

作用

  • 告诉 Jackson:反序列化对象时该构造函数或静态工厂方法可以用来创建对象。
  • 常用于:不可变对象(无 setter)、字段为 final、构造参数较复杂时。

示例一

构造函数 + @JsonProperty

1
2
3
4
5
6
7
8
9
10
11
12
public class User {

private final Long id;
private final String name;

@JsonCreator
public User(@JsonProperty("id") Long id,
@JsonProperty("name") String name) {
this.id = id;
this.name = "姓名:" + name;
}
}

示例二

静态工厂方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class User {

private Long id;
private String name;

@JsonCreator
public static User create(@JsonProperty("id") Long id,
@JsonProperty("name") String name) {
User user = new User();
user.id = id;
user.name = name;
return user;
}
}

动态属性处理

@JsonAnySetter

作用

  • 动态接收 JSON 中未知字段。
  • 当 JSON 中出现 POJO 未定义的字段时,用该方法接收这些字段。
  • 常用于:键值对类型对象、不确定字段结构场景。

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Data
@NoArgsConstructor
@AllArgsConstructor
@Accessors(chain = true)
public class DynamicUser {

private String name;
private Map<String, Object> extra = new HashMap<>();

@JsonAnySetter
public void setExtra(String key, Object value) {
extra.put(key, value);
}

}

JSON 输入:

1
2
3
4
5
{
"id": 1,
"name": "张三",
"age": 18
}

结果:

1
DynamicUser(name=张三, extra={id=1, age=18})
  • name → 映射到字段。
  • id / age → 存储哈希表 extra 中。

@JsonAnyGetter

作用

  • @JsonAnySetter 相对,用于将 Map 中的数据当作字段动态序列化输出。

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Data
@NoArgsConstructor
@AllArgsConstructor
@Accessors(chain = true)
public class Config {

private String name;

private Map<String, Object> settings = new HashMap<>();

@JsonAnyGetter
public Map<String, Object> getSettings() {
return settings;
}

}

设置数据:

1
2
3
4
Config cfg = new Config();
cfg.setName("系统配置");
cfg.getSettings().put("threadPool", 10);
cfg.getSettings().put("timeout", 5000);

输出为:

1
2
3
4
5
{
"name" : "系统配置",
"threadPool" : 10,
"timeout" : 5000
}

高级特性

Mix-in

定义

Mix-in 是 Jackson 提供的一种“影子注解”机制,可以让你在不修改原始类源码的情况下,给类“动态添加 Jackson 注解”。

使用场景

  • 第三方类无法添加注解(如 JDK 自带类、外部依赖类);
  • 你想将 Jackson 注解与业务类解耦(领域模型 vs 展示模型);
  • 多个上下文下使用同一类,但需要不同的序列化逻辑。

基本用法

  1. 假设目标类为第三方提供的类、且无法修改

    1
    2
    3
    4
    public class OnlyReadClass {
    private Long id;
    private String name;
    }
  2. 编写一个影子类(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;

    }

    注意影子类的属性应于源类的属性名称相同。

  3. 使用:在进行序列化或反序列化前,将目标类与影子类进行绑定

    1
    mapper.addMixIn(OnlyReadClass.class, OnlyReadClassMixIn.class);
  4. 之后影子类的序列化行为就等价于加上了影子类中的注解

    1
    2
    3
    OnlyReadClass onlyReadClass = new OnlyReadClass().setId(1L).setName("只读类");
    String json = mapper.writeValueAsString(onlyReadClass);
    // json为:{"or_id":1,"or_name":"只读类"}
    1
    2
    3
    String fromJson = "{\"or_id\":1,\"or_name\":\"只读类\"}";
    OnlyReadClass orClass = mapper.readValue(fromJson, OnlyReadClass.class);
    // orClass为:OnlyReadClass(id=1, name=只读类)

自定义序列化器

序列化器

场景

  • 特殊格式输出,如手机号脱敏、金额格式化。
  • 将对象转为特定 JSON 字符串。

示例

继承 JsonSerializer 类,重写序列化方法 serialize

1
2
3
4
5
6
7
public class PhoneNumberSerializer extends JsonSerializer<String> {
@Override
public void serialize(String value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
// 只保留后4位
gen.writeString("***" + value.substring(value.length() - 4));
}
}

使用

在类属性上通过 @JsonSerialize 指定该属性的序列化器:

1
2
@JsonSerialize(using = PhoneNumberSerializer.class)
private String phoneNumber;

反序列化器

场景

  • 将字符串转为枚举。
  • 将特殊格式(如前端传的 yyyyMMdd)转为 Java 时间对象。

示例

继承 JsonDeserializer 类,重写反序列化方法 deserialize

1
2
3
4
5
6
public class LocalDateYMDDeserializer extends JsonDeserializer<LocalDate> {
@Override
public LocalDate deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
return LocalDate.parse(p.getText(), DateTimeFormatter.ofPattern("yyyyMMdd"));
}
}

使用

在类属性上通过 @JsonDeserialize 指定该属性的反序列化器:

1
2
@JsonDeserialize(using = LocalDateYMDDeserializer.class)
private LocalDate birthday;

全局注册

可通过 SimpleModule 注册自定义的反序列化起到全局的 ObjectMapper

1
2
3
4
SimpleModule module = new SimpleModule();
module.addSerializer(Long.class, ToStringSerializer.instance);
module.addSerializer(String.class, new PhoneNumberSerializer());
objectMapper.registerModule(module);

注意事项

要点 说明
注解优先级高 如果字段上同时注册了 @JsonSerialize,则会覆盖全局注册。
建议封装公共序列化器 ToStringSerializerBigDecimalKeep2Serializer 等。
可结合 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
2
ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(new JavaTimeModule());

其他常见模块:

  • jackson-module-kotlin:Kotlin 支持。

  • jackson-module-parameter-names:支持构造函数参数名识别(配合 @JsonCreator)。

  • jackson-datatype-jdk8OptionalStream 支持。

  • jackson-module-afterburner:性能加速器(生成动态字节码)。

自定义模块

以加密手机号场景为例:

  1. 创建加密手机号类:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    @Accessors(chain = true)
    public class EncryptedPhone {

    private String originalNum;

    }
  2. 自定义加密手机号的序列化器 EncryptedPhoneSerializer,对手机号字段进行处理:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    public 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"));
    }
    }
  3. 自定义一个序列化修改器,为 EncryptedPhone 添加特定的序列化器

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    public 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;
    }
    }
  4. 使用时将序列化修改器注册到模块中:

    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));
  5. 运行程序,控制台输出加密手机号:

    1
    {"phone": "135****8901"}

视图与过滤器

@JsonView

作用

  • 控制字段在不同场景下是否参与序列化(类似权限视图、版本控制)。
  • 一般配合 ObjectMapper.writerWithView() 使用。
  • 默认 Jackson 序列化时不会使用视图,需显式调用。

示例

  1. 定义视图类:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    public class Views {

    public static class Public {
    }

    public static class Internal extends Public {
    }

    }
  2. 为属性标注视图:

    1
    2
    3
    4
    5
    @JsonView(Views.Public.class)
    public String publicField;

    @JsonView(Views.Internal.class)
    public String internalField;
  3. ObjectMapper 写出 JSON 前通过 writerWithView() 方法指定需要输出的特定视图:

    1
    2
    3
    4
    5
    ObjectMapper mapper = getCommonObjectMapper();
    User user = new User().setPublicField("p").setInternalField("i");
    String json = mapper.writerWithView(Views.Public.class)
    .writeValueAsString(user);
    System.out.println(json);
  4. 输出:

    1
    2
    3
    {
    "publicField" : "p"
    }

FilterProvider/@JsonFilter

作用

  • Jackson 过滤器机制允许在运行时动态决定输出哪些字段。
  • 通常用于字段级权限控制、字段白名单/黑名单、属性排除等。
  • @JsonView 更动态、更灵活(但也更复杂)。

示例

  1. 给实体类加上 @JsonFilter 注解:

    1
    2
    3
    4
    5
    @JsonFilter("userFilter")
    public class User {
    public String publicField;
    public String internalField;
    }
  2. 定义过滤器规则:

    1
    2
    SimpleBeanPropertyFilter filter = SimpleBeanPropertyFilter.filterOutAllExcept("publicField");
    SimpleFilterProvider userFilter = new SimpleFilterProvider().addFilter("userFilter", filter);
  3. 绑定过滤器到 ObjectMapper 写出 JSON:

    1
    2
    3
    User user = new User().setPublicField("p").setInternalField("i");
    String json = mapper.writer(userFilter).writeValueAsString(user);
    System.out.println(json);
  4. 输出:

    1
    2
    3
    {
    "publicField" : "p"
    }

对比

功能项 @JsonView @JsonFilter + FilterProvider
控制粒度 静态字段注解 运行时动态
使用方式 依赖注解 + writerWithView 依赖注解 + writer(filters)
灵活性
推荐场景 多角色/视图切换 字段权限、白名单/黑名单、动态字段输出
Spring 支持 完全支持(可直接 @JsonView 返回) 不直接支持,需手动控制输出

用法建议

场景 建议用法
管理后台接口和前台接口输出字段不同 @JsonView
某些字段要根据权限动态控制是否输出 @JsonFilter
想做统一的字段输出白名单机制 @JsonFilter
仅部分模型需要简单视图区分 @JsonView 更易用
复杂系统字段访问控制 搭配 Spring Security + @JsonFilter 实现

循环引用解决

父子结构中双向引用

1
2
3
4
5
6
7
8
9
public class Parent {
private String name;
private Child child;
}

public class Child {
private String name;
private Parent parent;
}

赋值:

1
2
3
4
5
6
Parent parent = new Parent();
Child child = new Child();
parent.setName("p");
parent.setChild(child);
child.setName("c");
child.setParent(parent);

序列化会导致无线递归:

1
Parent → Child → Parent → Child → ...

双向关系

利用 @JsonManagedReference/@JsonBackReference 解决。

作用

  • 标记父子关系的一方为“主动方”(序列化),另一方为“反向方”(不序列化);
  • 非对称:一个写出,另一个不写。

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
public class Parent {
private String name;

@JsonManagedReference // 主动序列化方
private Child child;
}

public class Child {
private String name;

@JsonBackReference // 反向引用,序列化时忽略
private Parent parent;
}

当序列化 Parent 时,能正确输出 child:

1
2
3
4
5
6
{
"name" : "p",
"child" : {
"name" : "c"
}
}

当序列化 Child 时,会忽略 parent:

1
2
3
{
"name" : "c"
}

基于ID追踪

通过 @JsonIdentityInfo 实现基于 ID 追踪对象引用。

作用

  • 给对象加一个 ID(通常是 @id),Jackson 会在后续引用中使用这个 ID 来代替完整对象,从而避免循环引用。
  • 可同时支持序列化和反序列化。

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
@Data
@NoArgsConstructor
@AllArgsConstructor
@Accessors(chain = true)
@JsonIdentityInfo(
generator = ObjectIdGenerators.PropertyGenerator.class,
property = "id"
)
public class Person {
private Long id;
private String name;
private List<Person> friends;
}

赋值:

1
2
3
4
Person a = new Person().setId(1L).setName("A");
Person b = new Person().setId(2L).setName("B");
a.setFriends(Collections.singletonList(b));
b.setFriends(Collections.singletonList(a));

序列化输出:

1
2
3
4
5
6
7
8
9
10
11
12
13
{
"id": 1,
"name": "A",
"friends": [
{
"id": 2,
"name": "B",
"friends": [
1
]
}
]
}

常见 ID 生成器

  • ObjectIdGenerators.PropertyGenerator:按属性(如 id 字段)。
  • ObjectIdGenerators.UUIDGenerator:自动 UUID。
  • ObjectIdGenerators.IntSequenceGenerator:递增整数。

流式处理

Streaming API 是 Jackson 最底层的处理方式,基于两个核心类:

  • JsonParser(读取 JSON)
  • JsonGenerator(写入 JSON)

它们是基于 token 流的模型(字段、值、开始/结束对象/数组等都作为独立 token 处理)。

特点

  • 内存占用极低:不构造对象,不构造树结构。
  • 性能最高:适合大文件、流式网络。
  • 编程复杂度高:需要手动管理 token 状态机。

读取示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
String json = "{\"id\":1,\"name\":\"张三\"}";

JsonFactory factory = new JsonFactory();
JsonParser parser = factory.createParser(json);

while (!parser.isClosed()) {
JsonToken token = parser.nextToken();

if (JsonToken.FIELD_NAME.equals(token)) {
String fieldName = parser.getCurrentName();
parser.nextToken(); // 移动到值
String value = parser.getText();

System.out.println(fieldName + " = " + value);
}
}

输出:

1
2
id = 1
name = 张三

写入示例

1
2
3
4
5
6
7
8
9
10
StringWriter writer = new StringWriter();
JsonGenerator gen = new JsonFactory().createGenerator(writer);

gen.writeStartObject();
gen.writeNumberField("id", 1);
gen.writeStringField("name", "李四");
gen.writeEndObject();
gen.close();

System.out.println(writer);

输出:

1
{"id":1,"name":"李四"}

非阻塞解析

从 Jackson 2.9+ 开始,引入非阻塞 JSON 解析器,可以处理异步数据流(如 WebSocket、Netty、NIO ByteBuffer)。

特点

  • 增量处理:支持一部分一部分地喂数据。
  • IO 解耦:非阻塞 IO 配合良好(如 Netty)。
  • 低延迟:可边接收边解析,无需等整个 JSON 到达。

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
byte[] jsonBytes = "{\"id\":1,\"name\":\"Leo\"}".getBytes(StandardCharsets.UTF_8);

JsonFactory factory = new JsonFactory();
NonBlockingJsonParser parser = (NonBlockingJsonParser) factory.createNonBlockingByteArrayParser();

// 一次性喂数据
parser.feedInput(jsonBytes, 0, jsonBytes.length);
parser.endOfInput(); // 告诉解析器数据喂完了

JsonToken token;
while ((token = parser.nextToken()) != null && token != JsonToken.NOT_AVAILABLE) {
if (token == JsonToken.FIELD_NAME) {
String field = parser.getCurrentName();
parser.nextToken(); // 获取字段值
String value = parser.getText();
System.out.println(field + " = " + value);
}
}

注意事项

  • feedInput(byte[], offset, len):喂数据之前,Jackson 必须消费完前一批数据。
  • 使用 parser.endOfInput():告诉解析器“没有更多了”,必要。
  • 不要重复喂未消费完的数据:否则会报错 Input start/end mismatch。

Spring Boot集成

默认配置入口

Spring Boot 自动装配 Jackson 的核心类是:

1
org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration

它会自动注册以下 Bean:

  • ObjectMapper:全局唯一,推荐注入使用
  • Jackson2ObjectMapperBuilder:构造器工厂,允许定制化配置
  • 配合 application.yml 中的 spring.jackson.* 属性自动生效

配置文件配置

1
2
3
4
5
6
7
8
9
10
spring:
jackson:
date-format: yyyy-MM-dd HH:mm:ss
time-zone: Asia/Shanghai
serialization:
indent-output: true
write-dates-as-timestamps: false
deserialization:
fail-on-unknown-properties: false
default-property-inclusion: non_null

自定义ObjectMapper

主要配置点

  • 忽略空值及空对象,不会因空对象报错。
  • 接收未知字段而不报错。
  • 枚举序列化方法重写,未知枚举值时反序列化为 null。
  • 关闭时间戳、指定时区。
  • Java 8 支持:注册 JavaTimeModuleJdk8Module 支持 LocalDateTimeOptional 等。
  • 数值精度保护:全局将 Long/long/BigDecimal 序列化为字符串 避免 JavaScript 精度丢失。
  • Map 排序保证输出结构稳定,可用于签名校验。
  • char[] 输出为数组。
  • 保持默认的自动探测(字段/Getter/Setter)+ 开启注解 兼容绝大多数 POJO,无需每个字段都打注解。

最终配置类如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
@Configuration
public class JacksonConfig {

@Bean("JacksonConfigObjectMapper")
@Primary
public ObjectMapper objectMapper() {
ObjectMapper mapper = new ObjectMapper();

// 忽略null值及空对象
mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
mapper.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS);
// 禁止反序列化未知属性异常
mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);

// 枚举
mapper.enable(SerializationFeature.WRITE_ENUMS_USING_TO_STRING);
mapper.enable(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_AS_NULL);

// 日期、模块处理
mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
mapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
mapper.setTimeZone(SimpleTimeZone.getTimeZone("Asia/Shanghai"));
JavaTimeModule dateTimeModel = new JavaTimeModule();
dateTimeModel.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
dateTimeModel.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
dateTimeModel.addSerializer(LocalDate.class, new LocalDateSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd")));
dateTimeModel.addDeserializer(LocalDate.class, new LocalDateDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd")));
dateTimeModel.addSerializer(LocalTime.class, new LocalTimeSerializer(DateTimeFormatter.ofPattern("HH:mm:ss")));
dateTimeModel.addDeserializer(LocalTime.class, new LocalTimeDeserializer(DateTimeFormatter.ofPattern("HH:mm:ss")));
mapper.registerModule(dateTimeModel);
mapper.registerModule(new Jdk8Module());

// 精度保护
SimpleModule simpleModule = new SimpleModule();
simpleModule.addSerializer(Long.class, ToStringSerializer.instance);
simpleModule.addSerializer(Long.TYPE, ToStringSerializer.instance);
simpleModule.addSerializer(BigDecimal.class, ToStringSerializer.instance);
mapper.registerModule(simpleModule);

// 顺序序列化Map
mapper.enable(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS);
// char数组作为JSON数组序列化
mapper.enable(SerializationFeature.WRITE_CHAR_ARRAYS_AS_JSON_ARRAYS);

// 启用注解以及自动检测字段
mapper.enable(MapperFeature.AUTO_DETECT_FIELDS);
mapper.enable(MapperFeature.AUTO_DETECT_GETTERS);
mapper.enable(MapperFeature.AUTO_DETECT_IS_GETTERS);
mapper.enable(MapperFeature.AUTO_DETECT_SETTERS);
mapper.enable(MapperFeature.USE_ANNOTATIONS);

return mapper;
}

}

工具类封装

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
public final class JacksonUtil {

private static volatile ObjectMapper mapper;

private static final Logger log = LoggerFactory.getLogger(JacksonUtil.class);

private JacksonUtil() {
}

/*================== Spring 注入 mapper ==================*/

/**
* 通过 Spring 自动注入全局 ObjectMapper 并填充静态变量
* 只在启动阶段执行一次,保证线程安全
*/
@Configuration(proxyBeanMethods = false)
@DependsOn("JacksonConfigObjectMapper")
static class JacksonUtilInitializer implements InitializingBean {

private final ObjectMapper injectedMapper;

JacksonUtilInitializer(@Qualifier("JacksonConfigObjectMapper") ObjectMapper injectedMapper) {
this.injectedMapper = injectedMapper;
}

@Override
public void afterPropertiesSet() {
JacksonUtil.mapper = this.injectedMapper;
}
}

/*================== 内部异常包装 ==================*/

public static class JacksonException extends RuntimeException {
public JacksonException(String msg, Throwable cause) {
super(msg, cause);
}
}

/**
* 获取ObjectMapper实例
*/
public static ObjectMapper getMapper() {
if (mapper == null) {
synchronized (JacksonUtil.class) {
if (mapper == null) {
mapper = SpringUtil.getBean("JacksonConfigObjectMapper", ObjectMapper.class);
}
}
}
return mapper;
}

/*================== 异常处理模板 ==================*/

/**
* 执行一个操作,如果抛出异常则返回默认值
*/
public static <T> T executeWithDefault(Supplier<T> supplier, T defaultValue) {
try {
return supplier.get();
} catch (Exception e) {
log.debug("操作失败,返回默认值,异常信息:", e);
return defaultValue;
}
}

/*================== 核心序列化 ==================*/

/**
* 对象序列化为JSON字符串(紧凑)
*/
public static String toJsonString(Object obj) {
return executeWithDefault(() -> toJsonStringOrThrow(obj), "{}");
}

/**
* 对象序列化为JSON字符串(紧凑),如果失败则抛出异常
*/
public static String toJsonStringOrThrow(Object obj) {
try {
return getMapper().writeValueAsString(obj);
} catch (Exception e) {
log.error("对象序列化为JSON字符串(紧凑)失败,obj = {}", obj, e);
throw new JacksonException("对象序列化为JSON字符串(紧凑)失败", e);
}
}

/**
* 对象序列化为JSON字符串(美化)
*/
public static String toPrettyJsonString(Object obj) {
return executeWithDefault(() -> toPrettyJsonStringOrThrow(obj), "{}");
}

/**
* 对象序列化为JSON字符串(美化),如果失败则抛出异常
*/
public static String toPrettyJsonStringOrThrow(Object obj) {
try {
return getMapper().writerWithDefaultPrettyPrinter().writeValueAsString(obj);
} catch (Exception e) {
log.error("对象序列化为JSON字符串(美化)失败,obj = {}", obj, e);
throw new JacksonException("对象序列化为JSON字符串(美化)失败", e);
}
}

/**
* 对象序列化为字节数组
*/
public static byte[] toJsonBytes(Object obj) {
return executeWithDefault(() -> toJsonBytesOrThrow(obj), new byte[0]);
}

/**
* 对象序列化为字节数组,如果失败则抛出异常
*/
public static byte[] toJsonBytesOrThrow(Object obj) {
try {
return getMapper().writeValueAsBytes(obj);
} catch (Exception e) {
log.error("对象序列化为字节数组失败,obj = {}", obj, e);
throw new JacksonException("对象序列化为字节数组失败", e);
}
}

/*================== 核心反序列化 ==================*/

/**
* JSON字符串反序列化为对象(简单类型)
*/
public static <T> T parseObject(String json, Class<T> clazz) {
return executeWithDefault(() -> parseObjectOrThrow(json, clazz), null);
}

/**
* JSON字符串反序列化为对象(简单类型),如果失败则抛出异常
*/
public static <T> T parseObjectOrThrow(String json, Class<T> clazz) {
try {
return getMapper().readValue(json, clazz);
} catch (Exception e) {
log.error("JSON字符串反序列化为对象(简单类型)失败,json = {}", json, e);
throw new JacksonException("JSON字符串反序列化为对象(简单类型)失败", e);
}
}

/**
* JSON字符串反序列化为对象(泛型TypeReference)
*/
public static <T> T parseObject(String json, TypeReference<T> typeRef) {
return executeWithDefault(() -> parseObjectOrThrow(json, typeRef), null);
}

/**
* JSON字符串反序列化为对象(泛型TypeReference),如果失败则抛出异常
*/
public static <T> T parseObjectOrThrow(String json, TypeReference<T> typeRef) {
try {
return getMapper().readValue(json, typeRef);
} catch (Exception e) {
log.error("JSON字符串反序列化为对象(泛型TypeReference)失败,json = {}", json, e);
throw new JacksonException("JSON字符串反序列化为对象(泛型TypeReference)失败", e);
}
}

/**
* 字节数组反序列化为对象(简单类型)
*/
public static <T> T parseObject(byte[] bytes, Class<T> clazz) {
return executeWithDefault(() -> parseObjectOrThrow(bytes, clazz), null);
}

/**
* 字节数组反序列化为对象(简单类型),如果失败则抛出异常
*/
public static <T> T parseObjectOrThrow(byte[] bytes, Class<T> clazz) {
try {
return getMapper().readValue(bytes, clazz);
} catch (Exception e) {
log.error("字节数组反序列化为对象(简单类型)失败,bytes = {}", bytes, e);
throw new JacksonException("字节数组反序列化为对象(简单类型)失败", e);
}
}

/**
* 字节数组反序列化为对象(泛型TypeReference)
*/
public static <T> T parseObject(byte[] bytes, TypeReference<T> typeRef) {
return executeWithDefault(() -> parseObjectOrThrow(bytes, typeRef), null);
}

/**
* 字节数组反序列化为对象(泛型TypeReference),如果失败则抛出异常
*/
public static <T> T parseObjectOrThrow(byte[] bytes, TypeReference<T> typeRef) {
try {
return getMapper().readValue(bytes, typeRef);
} catch (Exception e) {
log.error("字节数组反序列化为对象(泛型TypeReference)失败,bytes = {}", bytes, e);
throw new JacksonException("字节数组反序列化为对象(泛型TypeReference)失败", e);
}
}

/**
* JSON反序列化为列表(简单类型)
*/
public static <T> List<T> parseArray(String json, Class<T> clazz) {
return executeWithDefault(() -> parseArrayOrThrow(json, clazz), Collections.emptyList());
}

/**
* JSON反序列化为列表(简单类型),如果失败则抛出异常
*/
public static <T> List<T> parseArrayOrThrow(String json, Class<T> clazz) {
try {
return getMapper().readValue(json, getMapper().getTypeFactory().constructCollectionType(List.class, clazz));
} catch (Exception e) {
log.error("JSON反序列化为列表(简单类型)失败,json = {}", json, e);
throw new JacksonException("JSON反序列化为列表(简单类型)失败", e);
}
}

/**
* JSON反序列化为列表(泛型TypeReference)
*/
public static <T> List<T> parseArray(String json, TypeReference<T> typeRef) {
return executeWithDefault(() -> parseArrayOrThrow(json, typeRef), Collections.emptyList());
}

/**
* JSON反序列化为列表(泛型TypeReference),如果失败则抛出异常
*/
public static <T> List<T> parseArrayOrThrow(String json, TypeReference<T> typeRef) {
try {
List<?> tmp = getMapper().readValue(json, List.class);
return convertList(tmp, typeRef);
} catch (Exception e) {
log.error("JSON反序列化为列表(泛型TypeReference)失败,json = {}", json, e);
throw new JacksonException("JSON反序列化为列表(泛型TypeReference)失败", e);
}
}

/**
* 字节数组反序列化为列表(简单类型)
*/
public static <T> List<T> parseArray(byte[] bytes, Class<T> clazz) {
return executeWithDefault(() -> parseArrayOrThrow(bytes, clazz), Collections.emptyList());
}

/**
* 字节数组反序列化为列表(简单类型),如果失败则抛出异常
*/
public static <T> List<T> parseArrayOrThrow(byte[] bytes, Class<T> clazz) {
try {
return getMapper().readValue(bytes, getMapper().getTypeFactory().constructCollectionType(List.class, clazz));
} catch (Exception e) {
log.error("字节数组反序列化为列表(简单类型)失败,bytes = {}", bytes, e);
throw new JacksonException("字节数组反序列化为列表(简单类型)", e);
}
}

/**
* 字节数组反序列化为列表(泛型TypeReference)
*/
public static <T> List<T> parseArray(byte[] bytes, TypeReference<T> typeRef) {
return executeWithDefault(() -> parseArrayOrThrow(bytes, typeRef), null);
}

/**
* 字节数组反序列化为列表(泛型TypeReference),如果失败则抛出异常
*/
public static <T> List<T> parseArrayOrThrow(byte[] bytes, TypeReference<T> typeRef) {
try {
List<?> tmp = getMapper().readValue(bytes, List.class);
return convertListOrThrow(tmp, typeRef);
} catch (Exception e) {
log.error("字节数组反序列化为列表(泛型TypeReference)失败,bytes = {}", bytes, e);
throw new JacksonException("字节数组反序列化为列表(泛型TypeReference)失败", e);
}
}

/*================== 进阶功能 ==================*/

/**
* 对象深克隆(简单类型)
*/
public static <T> T clone(Object source, Class<T> clazz) {
return executeWithDefault(() -> cloneOrThrow(source, clazz), null);
}

/**
* 对象深克隆(简单类型),如果失败则抛出异常
*/
public static <T> T cloneOrThrow(Object source, Class<T> clazz) {
try {
return parseObjectOrThrow(toJsonString(source), clazz);
} catch (Exception e) {
log.error("对象深克隆(简单类型)失败,source = {}", source, e);
throw new JacksonException("对象深克隆(简单类型)失败", e);
}
}

/**
* 对象深克隆(泛型TypeReference)
*/
public static <T> T clone(Object source, TypeReference<T> typeRef) {
return executeWithDefault(() -> cloneOrThrow(source, typeRef), null);
}

/**
* 对象深克隆(泛型TypeReference),如果失败则抛出异常
*/
public static <T> T cloneOrThrow(Object source, TypeReference<T> typeRef) {
try {
return parseObjectOrThrow(toJsonString(source), typeRef);
} catch (Exception e) {
log.error("对象深克隆(泛型TypeReference)失败,source = {}", source, e);
throw new JacksonException("对象深克隆(泛型TypeReference)失败", e);
}
}

/**
* 对象转换(简单类型)
*/
public static <T> T convert(Object source, Class<T> targetType) {
return executeWithDefault(() -> convertOrThrow(source, targetType), null);
}

/**
* 对象转换(简单类型),如果失败则抛出异常
*/
public static <T> T convertOrThrow(Object source, Class<T> targetType) {
try {
return getMapper().convertValue(source, targetType);
} catch (Exception e) {
log.error("对象转换(简单类型)失败,source = {}", source, e);
throw new JacksonException("对象转换(简单类型)失败", e);
}
}

/**
* 对象转换(泛型TypeReference)
*/
public static <T> T convert(Object source, TypeReference<T> typeRef) {
return executeWithDefault(() -> convertOrThrow(source, typeRef), null);
}

/**
* 对象转换(泛型TypeReference),如果失败则抛出异常
*/
public static <T> T convertOrThrow(Object source, TypeReference<T> typeRef) {
try {
return getMapper().convertValue(source, typeRef);
} catch (Exception e) {
log.error("对象转换(泛型TypeReference)失败,source = {}", source, e);
throw new JacksonException("对象转换(泛型TypeReference)失败", e);
}
}

/**
* 列表转换(简单类型)
*/
public static <T> List<T> convertList(List<?> source, Class<T> targetType) {
return executeWithDefault(() -> convertListOrThrow(source, targetType), Collections.emptyList());
}

/**
* 列表转换(简单类型),如果失败则抛出异常
*/
public static <T> List<T> convertListOrThrow(List<?> source, Class<T> targetType) {
try {
return getMapper().convertValue(source, getMapper().getTypeFactory().constructCollectionType(List.class, targetType));
} catch (Exception e) {
log.error("列表转换(简单类型)失败,source = {}", source, e);
throw new JacksonException("列表转换(简单类型)失败", e);
}
}

/**
* 列表转换(泛型TypeReference)
*/
public static <T> List<T> convertList(List<?> source, TypeReference<T> typeRef) {
return executeWithDefault(() -> convertListOrThrow(source, typeRef), Collections.emptyList());
}

/**
* 列表转换(泛型TypeReference),如果失败则抛出异常
*/
public static <T> List<T> convertListOrThrow(List<?> source, TypeReference<T> typeRef) {
try {
if (source.isEmpty()) {
return Collections.emptyList();
}
return source.stream()
.map(item -> getMapper().convertValue(item, typeRef))
.collect(Collectors.toList());
} catch (Exception e) {
log.error("列表转换(泛型TypeReference)失败,source = {}", source, e);
throw new JacksonException("列表转换(泛型TypeReference)失败", e);
}
}

/**
* JSON增量更新对象
*/
public static <T> T update(String jsonPatch, T target) {
return executeWithDefault(() -> updateOrThrow(jsonPatch, target), null);
}

/**
* JSON增量更新对象,如果失败则抛出异常
*/
public static <T> T updateOrThrow(String jsonPatch, T target) {
try {
ObjectReader updater = getMapper().readerForUpdating(target);
return updater.readValue(jsonPatch);
} catch (Exception e) {
log.error("JSON增量更新对象失败,jsonPatch = {}, target = {}", jsonPatch, target, e);
throw new JacksonException("JSON增量更新对象失败", e);
}
}

/**
* JSON解析为树模型
*/
public static JsonNode parseTree(String json) {
return executeWithDefault(() -> parseTreeOrThrow(json), null);
}

/**
* JSON解析为树模型,如果失败则抛出异常
*/
public static JsonNode parseTreeOrThrow(String json) {
try {
return getMapper().readTree(json);
} catch (Exception e) {
log.error("JSON解析为树模型失败,json = {}", json, e);
throw new JacksonException("JSON解析为树模型失败", e);
}
}

/**
* 判断是否为合法的JSON字符串
*/
public static boolean isValidJson(String json) {
try {
getMapper().readTree(json);
return true;
} catch (Exception e) {
log.warn("无效的JSON字符串:{}", json, e);
return false;
}
}

}

总结

Jackson 不仅是一个强大的 JSON 序列化工具,更是现代 Java 项目中不可或缺的基础组件。通过合理配置 ObjectMapper、掌握核心注解、灵活使用模块机制,开发者可以实现对各种复杂数据结构、时间格式、枚举、多态等场景的精准控制。无论是处理简单对象,还是构建可拓展的框架工具类,Jackson 都提供了足够的能力与自由度。本文的讲解能协助开发者从底层原理到高级技巧全面掌握 Jackson,使 JSON 数据交互更安全、高效、优雅。


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!