Jackson 完全指南:从入门到精通

做 Java 后端的,没人没碰过 JSON。而只要碰 JSON,基本绕不开 Jackson。

这篇文章是我踩了无数坑之后写的,把 Jackson 的核心原理、常用注解、日期时间处理、性能优化、常见坑点全部梳理了一遍,配合图表尽量说清楚。


一、Jackson 是什么?

Jackson 是 Java 生态里用得最多的 JSON 处理库,Spring Boot 默认就用的它。干的事情很简单:Java 对象和 JSON 之间互相转换

Jackson

核心功能

序列化 Java→JSON

反序列化 JSON→Java

树模型遍历

流式读写

三大模块

core 核心引擎

databind 数据绑定

annotations 注解

扩展生态

JavaTimeModule 日期时间

ParameterNamesModule 参数名

Jdk8Module Optional/Stream

HibernateModule 懒加载

应用场景

REST API

消息队列

配置文件

数据存储

外部接口调用

市场地位

库名 市占率 Spring Boot 默认 维护状态
Jackson ~80% 活跃
Gson ~10% 维护中
Fastjson ~5% 漏洞多,不推荐
JsonB ~3% 低活跃

在 Java 世界里用 JSON,默认选 Jackson 就对了。


二、核心概念:序列化与反序列化

Jackson 只做两件事——把 Java 对象变成 JSON,和把 JSON 变回 Java 对象。

最简代码

ObjectMapper mapper = new ObjectMapper();

// 序列化:Java → JSON
User user = new User("小明", 25);
String json = mapper.writeValueAsString(user);
// {"name":"小明","age":25}

// 反序列化:JSON → Java
String json = "{\"name\":\"小明\",\"age\":25}";
User user = mapper.readValue(json, User.class);
// User{name='小明', age=25}

序列化的完整调用链

写一行 writeValueAsString(),背后其实做了不少事:

JSON 输出 JsonGenerator BeanPropertyWriter SerializerFactory ObjectMapper JSON 输出 JsonGenerator BeanPropertyWriter SerializerFactory ObjectMapper alt [有自定义序列化器] [默认序列化器] loop [每个属性] 开发者 writeValueAsString(obj) createBuffer() 分配写缓冲 findSerializer(class) CustomSerializer 扫描字段 + 注解 为每个属性创建 Writer BeanSerializer createGenerator(buffer) writeStartObject() serializeAsField(obj, gen) writeFieldName("name") writeString("小明") writeEndObject() buffer.toString() {"name":"小明","age":25} 开发者

三、ObjectMapper:Jackson 的核心引擎

ObjectMapper 是 Jackson 的心脏,所有操作都通过它完成。

核心类体系

注册扩展

readTree()

ObjectMapper

-SerializationConfig _serializationConfig

-DeserializationConfig _deserializationConfig

-SerializerFactory _serializerFactory

-DeserializerFactory _deserializerFactory

+writeValueAsString(Object) : String

+readValue(String, Class<T>) : T

+readValue(String, TypeReference<T>) : T

+readTree(String) : JsonNode

+registerModule(Module) : ObjectMapper

+configure(feature, bool) : ObjectMapper

+enable(feature) : ObjectMapper

+disable(feature) : ObjectMapper

+copy() : ObjectMapper

«interface»

Module

+getModuleName() : String

+setupModule(SetupContext) : void

JavaTimeModule

+支持 LocalDateTime

+支持 LocalDate

+支持 ZonedDateTime

+支持 Duration

SimpleModule

+addSerializer(type, ser)

+addDeserializer(type, deser)

«abstract»

JsonNode

+get(fieldName) : JsonNode

+asText() : String

+asInt() : int

+isArray() : bool

+fields() : Iterator

ObjectNode

+put(key, value)

+putArray(key)

+putObject(key)

ArrayNode

+add(value)

+get(index) : JsonNode

ObjectMapper 是重量级对象,别每次都 new

这是个很多人忽略的点。new ObjectMapper() 并不轻量——它要初始化序列化/反序列化工厂、扫描注解、缓存反射信息、分配内部缓冲区。首次构建大概 50ms,内存占用 200KB 起步。

new ObjectMapper()

做了什么?

初始化序列化工厂
扫描注解、缓存反射信息

初始化反序列化工厂
构建类型解析器

注册默认模块
ParameterNamesModule 等

分配内部缓冲区
CharBuffers / ByteBuffers

代价

首次构建耗时约50ms

内存占用约200KB起

内部缓存随使用增长

正确做法

全局复用,不要每次 new

Spring 中用 Bean 注入

需要不同配置时用 .copy()

// 反面教材:每次请求都 new
public String toJson(Object obj) {
    return new ObjectMapper().writeValueAsString(obj);
}

// 正确做法:全局复用
private static final ObjectMapper MAPPER = new ObjectMapper();

// 更好:Spring Bean
@Bean
public ObjectMapper objectMapper() {
    return new ObjectMapper()
        .registerModule(new JavaTimeModule())
        .disable(WRITE_DATES_AS_TIMESTAMPS);
}

ObjectMapper 本身是线程安全的,放心复用。

常用配置一览

反序列化配置

FAIL_ON_UNKNOWN_PROPERTIES
未知字段是否报错

ACCEPT_SINGLE_VALUE_AS_ARRAY
单值当数组

ACCEPT_EMPTY_STRING_AS_NULL_OBJECT
空串当null

READ_ENUMS_USING_TO_STRING
枚举用toString

UNWRAP_SINGLE_VALUE_ARRAYS
单值数组解包

序列化配置

INDENT_OUTPUT
美化输出

FAIL_ON_EMPTY_BEANS
空对象是否报错

WRITE_DATES_AS_TIMESTAMPS
日期格式

WRITE_NULL_MAP_VALUES
null值是否写出

ORDER_MAP_ENTRIES_BY_KEYS
按键排序

ObjectMapper mapper = new ObjectMapper()
    // 序列化
    .enable(SerializationFeature.INDENT_OUTPUT)
    .disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)
    .disable(SerializationFeature.FAIL_ON_EMPTY_BEANS)
    // 反序列化
    .disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)
    .enable(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY)
    .enable(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT);

这里面最常改的就两个:FAIL_ON_UNKNOWN_PROPERTIES(建议关掉,不然前后端字段不同步就炸)和 WRITE_DATES_AS_TIMESTAMPS(建议关掉,后面会说)。


四、注解体系:精确控制每一个字段

Jackson 的注解非常多,但日常用到的就那么几个。我按用途分了下类:

Jackson 注解体系

命名控制

可见性控制

类型控制

格式控制

引用控制

自定义序列化

@JsonProperty
指定JSON字段名

@JsonNaming
命名策略

@JsonIgnore
忽略字段

@JsonIgnoreProperties
类级忽略

@JsonInclude
控制null值

@JsonView
视图过滤

@JsonTypeInfo
多态类型信息

@JsonSubTypes
子类型映射

@JsonCreator
自定义构造

@JsonFormat
日期/数字格式

@JsonRawValue
原始JSON嵌入

@JsonValue
整个对象当单值

@JsonManagedReference
正向引用

@JsonBackReference
反向引用

@JsonIdentityInfo
对象ID去重

@JsonSerialize
指定序列化器

@JsonDeserialize
指定反序列化器

@JsonAlias
反序列化别名

高频注解实战

@JsonProperty — 字段重命名

前后端命名规范不同时经常用。Java 这边叫 name,JSON 那边叫 user_name

public class User {
    @JsonProperty("user_name")
    private String name;

    @JsonProperty("user_age")
    private int age;
}
// 输出:{"user_name":"小明","user_age":25}
@JsonIgnore / @JsonIgnoreProperties — 忽略字段

密码这种敏感字段,序列化时肯定不能让它出去:

@JsonIgnoreProperties({"password", "secret"})  // 类级忽略
public class User {
    private String name;
    @JsonIgnore
    private String password;
    private String secret;
}
// 输出:{"name":"小明"}
@JsonInclude — 控制空值输出

默认情况下 null 字段也会输出,很多时候不需要:

User{name='小明', age=0, hobby=null}

ALWAYS → 全部输出

NON_NULL → 去掉hobby

NON_EMPTY → 去掉hobby和age=0

NON_DEFAULT → 去掉age=0和hobby

@JsonInclude 策略

ALWAYS
总是输出(含null)

NON_NULL
非null才输出

NON_EMPTY
非空才输出

NON_DEFAULT
非默认值才输出

NON_ABSENT
非absent才输出

@JsonInclude(JsonInclude.Include.NON_NULL)
public class User {
    private String name;
    private String hobby;
}
// hobby=null → {"name":"小明"}
// hobby="篮球" → {"name":"小明","hobby":"篮球"}

实际项目中用 NON_NULL 最多,省得前端一堆 xxx: null

@JsonFormat — 日期格式化
public class Event {
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "Asia/Shanghai")
    private LocalDateTime startTime;

    @JsonFormat(pattern = "yyyy-MM-dd")
    private LocalDate birthday;
}
// {"startTime":"2024-05-08 10:30:00","birthday":"2000-01-15"}

注意 timezone 参数,不加的话可能跟你想的不一样。

@JsonCreator — 自定义构造

不可变对象没有 setter,反序列化时 Jackson 不知道怎么赋值,就得用 @JsonCreator 告诉它:

public class Money {
    private final BigDecimal amount;
    private final String currency;

    @JsonCreator
    public Money(
        @JsonProperty("amount") BigDecimal amount,
        @JsonProperty("currency") String currency
    ) {
        this.amount = amount;
        this.currency = currency;
    }
}
@JsonTypeInfo + @JsonSubTypes — 多态序列化

接口或抽象类在序列化时会丢掉实际类型信息,反序列化时 Jackson 不知道该实例化哪个子类。这两个注解就是来解决这个问题的:

反序列化:自动识别子类型

读取 type 字段

路由到对应子类

Circle 或 Rectangle 实例

序列化输出(含类型信息)

type=circle, radius=5.0

type=rectangle, width=10, height=20

Java 继承体系

Shape 抽象类

Circle
radius=5.0

Rectangle
width=10, height=20

@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "type")
@JsonSubTypes({
    @JsonSubTypes.Type(value = Circle.class, name = "circle"),
    @JsonSubTypes.Type(value = Rectangle.class, name = "rectangle")
})
public abstract class Shape { }

Shape shape = mapper.readValue(json, Shape.class);
// 自动根据 "type" 字段选择 Circle 或 Rectangle

五、日期时间处理:最大的坑

这块我踩的坑最多。Java 8 引入了 java.time,但 Jackson 默认不认识 LocalDateTime 这些类型,直接序列化会报错。

痛点与解决

没有

默认开启

关闭

LocalDateTime 序列化

有 JavaTimeModule?

报错!
Java 8 date/time type not supported

WRITE_DATES_AS_TIMESTAMPS?

输出时间戳
1715145600000
前端拿到一堆数字,不好处理

输出 ISO 格式
2024-05-08T10:30:00
前端直接用 Day.js 解析

前端还得额外转换

开箱即用

三种解决方式

方式 1:全局配置(推荐)

在 Spring Boot 项目里加一个配置类就够了:

@Configuration
public class JacksonConfig {
    @Bean
    public Jackson2ObjectMapperBuilderCustomizer jacksonCustomizer() {
        return builder -> builder
            .modulesToInstall(new JavaTimeModule())
            .featuresToDisable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
    }
}

这样整个项目的 ObjectMapper 都会带上这两个配置,不用每个地方手动加。

方式 2:字段级注解

只需要改个别字段的格式时用:

public class Event {
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
    private LocalDateTime createdAt;
}

方式 3:手动注册模块

不用 Spring 管理 ObjectMapper 时:

ObjectMapper mapper = new ObjectMapper()
    .registerModule(new JavaTimeModule())
    .disable(WRITE_DATES_AS_TIMESTAMPS);

Spring Boot 自动配置链路

加了这个 JacksonConfig 之后,整个配置是怎么生效的?

RestController ObjectMapper Bean JacksonConfig Spring Container RestController ObjectMapper Bean JacksonConfig Spring Container 之后所有 Controller 自动使用配置好的 ObjectMapper 应用启动 HTTP 请求 扫描 Configuration 发现 jacksonCustomizer() 创建默认 ObjectMapper 应用 Customizer\n注册 JavaTimeModule\n禁用 TIMESTAMPS 注册 ObjectMapper 为 Bean 注入 ObjectMapper POST /api/chat RequestBody 自动反序列化 Java 对象 ResponseBody 自动序列化 JSON 响应 应用启动 HTTP 请求

关键点:Spring 自动注入的 ObjectMapper 和 Controller 里 @RequestBody/@ResponseBody 用的是同一个实例,所以 JacksonConfig 的配置对整个项目生效。


六、高级特性

1. 泛型反序列化 — TypeReference

这个坑很多人踩过。因为 Java 泛型擦除,readValue(json, List.class) 拿到的其实是 List<LinkedHashMap>,不是 List<User>

// 类型擦除:运行时 List.class 不携带元素类型
List<User> users = mapper.readValue(json, List.class);  // 返回 List<LinkedHashMap>

// 用 TypeReference 保留泛型
List<User> users = mapper.readValue(json, new TypeReference<List<User>>() {});

// 嵌套泛型也没问题
Map<String, List<User>> map = mapper.readValue(json,
    new TypeReference<Map<String, List<User>>>() {});

2. 树模型 — JsonNode

有时候 JSON 结构不确定,或者只想取里面几个字段,定义 Java 类太麻烦。这时候可以直接用 JsonNode 操作,类似前端操作 JSON 对象:

JSON 字符串

mapper.readTree(json)

JsonNode 树

ObjectNode
{name: ...}

name: '小明'
asText() 返回 '小明'

scores: 数组
isArray() 返回 true

85
asInt() 返回 85

92
asInt() 返回 92

可修改
put / add / remove

mapper.writeValueAsString(node)
生成新 JSON

ObjectMapper mapper = new ObjectMapper();
JsonNode root = mapper.readTree(json);

// 读取
String name = root.get("name").asText();
int score = root.get("scores").get(0).asInt();
boolean hasAge = root.has("age");

// 遍历
root.fields().forEachRemaining(entry -> {
    System.out.println(entry.getKey() + " = " + entry.getValue());
});

// 修改
ObjectNode obj = (ObjectNode) root;
obj.put("age", 25);
obj.remove("name");
obj.putArray("tags").add("VIP").add("活跃");

String newJson = mapper.writeValueAsString(obj);

3. 自定义序列化器 — JsonSerializer

有些业务场景需要特殊的序列化逻辑,比如金额想输出 "CNY 99.9" 而不是 {"amount":99.9,"currency":"CNY"}

渲染错误: Mermaid 渲染失败: Parse error on line 4: ...["默认序列化\n{\"amount\":99.9,\"currency\":\ -----------------------^ Expecting 'SQE', 'DOUBLECIRCLEEND', 'PE', '-)', 'STADIUMEND', 'SUBROUTINEEND', 'PIPE', 'CYLINDEREND', 'DIAMOND_STOP', 'TAGEND', 'TRAPEND', 'INVTRAPEND', 'UNICODE_TEXT', 'TEXT', 'TAGSTART', got 'STR'
public class MoneySerializer extends JsonSerializer<Money> {
    @Override
    public void serialize(Money value, JsonGenerator gen, SerializerProvider provider)
            throws IOException {
        gen.writeString(value.getCurrency() + " " + value.getAmount());
    }
}

// 注册方式一:注解(只对这一个字段生效)
public class Order {
    @JsonSerialize(using = MoneySerializer.class)
    private Money price;
}

// 注册方式二:模块(全局生效)
SimpleModule module = new SimpleModule();
module.addSerializer(Money.class, new MoneySerializer());
mapper.registerModule(module);

4. 自定义反序列化器 — JsonDeserializer

反过来,从 "CNY 99.9" 还原成 Money 对象:

public class MoneyDeserializer extends JsonDeserializer<Money> {
    @Override
    public Money deserialize(JsonParser p, DeserializationContext ctx)
            throws IOException {
        String text = p.getValueAsString();  // "CNY 99.9"
        String[] parts = text.split(" ");
        return new Money(new BigDecimal(parts[1]), parts[0]);
    }
}

public class Order {
    @JsonDeserialize(using = MoneyDeserializer.class)
    private Money price;
}

5. 三种数据处理模式对比

Jackson 其实有三种操作 JSON 的方式,日常用数据绑定就够了,但另外两种各有适用场景:

Jackson 三种模式

数据绑定模式
最常用

树模型模式
灵活探索

流式模式
极致性能

readValue(json, Class)
writeValueAsString(obj)

优点:类型安全、简洁

缺点:需要定义 Java 类

readTree(json) → JsonNode

优点:无需预定义类、可动态遍历

缺点:无编译期类型检查

JsonFactory → JsonParser/Generator

优点:内存极低、速度最快

缺点:代码冗长、需手动处理token

模式 适用场景 内存占用 代码量
数据绑定 已知 JSON 结构
树模型 不确定 JSON 结构、需要动态探索
流式 大文件处理、极致性能要求 极低

流式模式平时很少用到,除非你在处理几百 MB 的 JSON 文件,需要逐 token 读取而不是一口气全加载到内存。


七、性能优化

性能对比

操作 Jackson Gson Fastjson2
序列化 (ops/ms) ~850 ~400 ~750
反序列化 (ops/ms) ~600 ~300 ~650
内存占用
大对象(>1MB) 稳定 较慢 稳定

数据来自社区基准测试,具体数值因场景而异,但相对排名基本稳定:Jackson 和 Fastjson2 第一梯队,Gson 明显慢一截。

优化技巧

Jackson 性能优化

1. 复用 ObjectMapper

2. 直接写到 OutputStream

3. 合理配置 Feature

4. 避免 String 中转

ObjectMapper 线程安全
可全局复用
避免重复初始化开销

writeValue(OutputStream, obj)
比 writeValueAsString() 少一次
String → byte[] 拷贝

关闭 INDENT_OUTPUT
关闭 FAIL_ON_UNKNOWN_PROPERTIES
按需开启

直接 writeValue(outputStream)
而非先 toString 再 getBytes

这里最容易忽视的是第 4 点。很多人习惯先 writeValueAsString()getBytes() 写到 response,其实多了一次不必要的内存拷贝:

// 慢:多余的字符串拷贝
String json = mapper.writeValueAsString(obj);
response.getOutputStream().write(json.getBytes(StandardCharsets.UTF_8));

// 快:直接写到输出流
mapper.writeValue(response.getOutputStream(), obj);

八、常见坑与排雷指南

我把平时遇到最多的几个问题画了个决策树,碰到报错可以顺着往下找:

遇到问题

反序列化报错
Unrecognized field?

关闭 FAIL_ON_UNKNOWN_PROPERTIES
或加 @JsonIgnoreProperties(ignoreUnknown=true)

日期序列化
输出时间戳?

注册 JavaTimeModule
+ 禁用 WRITE_DATES_AS_TIMESTAMPS

泛型丢失
变成 LinkedHashMap?

用 TypeReference
new TypeReference[List[User]](){}

循环引用
StackOverflow?

@JsonIgnore 打断环
或 @JsonManagedReference/@JsonBackReference

null 值污染 JSON?

@JsonInclude(NON_NULL)
类级或全局配置

字段名不匹配?

@JsonProperty
或 @JsonAlias

检查 ObjectMapper 配置
确认注册了所需 Module

逐个展开

坑 1:未知字段报错

前后端字段不同步是家常便饭。后端加了新字段,老版本前端没有,反序列化直接报错。建议全局关掉这个校验:

mapper.disable(FAIL_ON_UNKNOWN_PROPERTIES);

// 或者只对某个类生效
@JsonIgnoreProperties(ignoreUnknown = true)
public class User { ... }

坑 2:日期时间序列化成数字

LocalDateTime 不注册 JavaTimeModule 就报错,注册了不关 TIMESTAMPS 就输出 1715145600000 这种时间戳,前端拿到一脸懵:

mapper.registerModule(new JavaTimeModule());
mapper.disable(WRITE_DATES_AS_TIMESTAMPS);

坑 3:泛型擦除变成 LinkedHashMap

Java 泛型在运行时会被擦除,List<User> 传进去运行时只剩 List,Jackson 只好给你塞 LinkedHashMap:

List<User> users = mapper.readValue(json, List.class);  // 拿到 List<LinkedHashMap>

List<User> users = mapper.readValue(json,
    new TypeReference<List<User>>() {});  // 拿到真正的 List<User>

坑 4:循环引用导致 StackOverflow

双向关联的对象,序列化 A 会去序列化 B,序列化 B 又回到 A,无限递归:

public class User {
    @JsonIgnore  // 打断循环
    private List<Order> orders;
}

更优雅的方式是用 @JsonManagedReference + @JsonBackReference,在正向序列化时正常输出,反向时自动跳过。

坑 5:每次 new ObjectMapper

前面说过,不再赘述。一句话:全局复用。

坑 6:Spring Bean 和手动 new 的 ObjectMapper 不一致

这是我亲身踩过的坑。Spring 容器里的 ObjectMapper 注册了 JavaTimeModule、禁用了 TIMESTAMPS,但某个 Service 里手动 new ObjectMapper() 啥都没配,日期序列化出来的格式完全不一样,排查了半天。

// 项目中常见的错误
private static final ObjectMapper mapper = new ObjectMapper();

// 正确做法:注入 Spring 管理的
@Autowired
private ObjectMapper objectMapper;

九、实战:在本项目中的应用

拿我最近在做的项目举例,Jackson 在整个数据流中无处不在:

基础设施层

LLM API 调用

Agent 服务层

Spring Controller

前端

HTTP 请求 JSON

HTTP 响应 JSON

RequestBody 反序列化
Spring 自动用 ObjectMapper

ResponseBody 序列化
Spring 自动用 ObjectMapper

Planner 解析
readValue → 执行计划列表

偏好提取
readValue → Map

实体抽取
readTree → JsonNode

快照序列化
writeValueAsString → TaskState

构建请求体
writeValueAsString → JSON

解析响应
readTree → JsonNode

PG 存向量
writeValueAsString → embedding JSON

Kafka 事件
writeValueAsString → event payload

具体用法

// 解析 LLM Planner 返回的执行计划
List<Map<String, Object>> items = mapper.readValue(raw,
    mapper.getTypeFactory().constructCollectionType(List.class, Map.class));

// 解析偏好提取结果
Map<String, String> kvs = mapper.readValue(raw, Map.class);

// 解析实体关系抽取 — 不确定 LLM 返回什么,用 readTree 安全些
JsonNode root = mapper.readTree(raw);
JsonNode entities = root.get("entities");

// Embedding 向量存入 PG — 向量是 List<Double>,存成 JSONB
String embJson = mapper.writeValueAsString(embedding);

// 任务快照 — 用序列化/反序列化做深拷贝
String json = mapper.writeValueAsString(currentTask);
TaskState copy = mapper.readValue(json, TaskState.class);

// Kafka 事件
String eventData = mapper.writeValueAsString(Map.of("query", query, "mode", mode));

项目中存在的问题

之前 review 代码时发现,这个项目手动创建了 5 个 ObjectMapper 实例,配置还不一致:

// UnifiedAgentService.java — 注册了 JavaTimeModule
private static final ObjectMapper mapper = new ObjectMapper()
    .registerModule(new JavaTimeModule());

// InfrastructureService.java — 注册了 JavaTimeModule
private static final ObjectMapper mapper = new ObjectMapper()
    .registerModule(new JavaTimeModule());

// LlmService.java — 啥都没注册
private static final ObjectMapper mapper = new ObjectMapper();

// ToolService.java — 啥都没注册
private static final ObjectMapper mapper = new ObjectMapper();

// HybridStore.java — 啥都没注册
private static final ObjectMapper mapper = new ObjectMapper();

后面三个碰到 LocalDateTime 就会炸。改成统一注入 Spring Bean 就好了:

@Service
public class LlmService {
    private final ObjectMapper mapper;

    public LlmService(ObjectMapper mapper) {
        this.mapper = mapper;
    }
}

十、速查表

常用 API

操作 方法
对象 → JSON 字符串 mapper.writeValueAsString(obj)
对象 → OutputStream mapper.writeValue(outStream, obj)
JSON 字符串 → 对象 mapper.readValue(json, MyClass.class)
JSON 字符串 → 泛型对象 mapper.readValue(json, new TypeReference<List<MyClass>>(){})
JSON 字符串 → 树 mapper.readTree(json)
对象深拷贝 mapper.readValue(mapper.writeValueAsString(obj), MyClass.class)
美化输出 mapper.enable(INDENT_OUTPUT)
忽略未知字段 mapper.disable(FAIL_ON_UNKNOWN_PROPERTIES)
注册模块 mapper.registerModule(new JavaTimeModule())

注解速查

注解 作用 示例
@JsonProperty 指定 JSON 字段名 @JsonProperty("user_name")
@JsonIgnore 忽略字段 @JsonIgnore private String password;
@JsonIgnoreProperties 类级忽略 @JsonIgnoreProperties({"password"})
@JsonInclude 控制空值 @JsonInclude(NON_NULL)
@JsonFormat 日期格式 @JsonFormat(pattern="yyyy-MM-dd")
@JsonCreator 自定义构造 @JsonCreator public Obj(@JsonProperty(...) ...)
@JsonSerialize 自定义序列化器 @JsonSerialize(using = XSerializer.class)
@JsonDeserialize 自定义反序列化器 @JsonDeserialize(using = XDeserializer.class)
@JsonTypeInfo 多态类型信息 @JsonTypeInfo(use=Id.NAME, property="type")
@JsonSubTypes 子类型映射 @JsonSubTypes.Type(value=Circle.class, name="circle")
@JsonAlias 反序列化别名 @JsonAlias({"userName", "user_name"})
@JsonRawValue 原始 JSON @JsonRawValue String config;
@JsonValue 整个对象当单值 @JsonValue String getCode()
@JsonView 视图过滤 @JsonView(Views.Public.class)

模块速查

模块 依赖 作用
JavaTimeModule jackson-datatype-jsr310 支持 java.time
Jdk8Module jackson-datatype-jdk8 支持 OptionalStream
ParameterNamesModule jackson-module-parameter-names 构造器参数名推断
HibernateModule jackson-datatype-hibernate 处理 Hibernate 懒加载
KotlinModule jackson-module-kotlin 支持 Kotlin 数据类
BlackbirdModule jackson-module-blackbird 编译期优化
AfterburnerModule jackson-module-afterburner 字节码增强

总结

Jackson 要点

核心原则

ObjectMapper 全局复用

用 Spring 管理的实例

统一配置避免不一致

最常用的3件事

序列化 writeValueAsString

反序列化 readValue

树操作 readTree

最容易踩的3个坑

日期时间必须注册 JavaTimeModule

泛型必须用 TypeReference

未知字段必须关闭 FAIL_ON_UNKNOWN

最佳实践

注解优先于手动代码

Module 扩展优先于自定义序列化器

注入 Bean 优先于 new ObjectMapper

用好 Jackson 说到底就三件事:复用 ObjectMapper、注册需要的 Module、善用注解控制细节。把这三点做到,基本就不会踩坑了。

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐