概述
Jackson是Java生态老牌的JSON编解码库,它凭借其稳健的代码设计、活跃的社区生态,赢得了一众Java开发者的青睐,成为Spring生态里默认的、开箱即用的JSON编解码库。

本文将自顶向下的介绍Jackson是如何完成【string】->【object】转换,帮助大家更好地理解Jackson反序列化的【宏观脉络】。
代码架构
随便找一个使用了Jackson的业务服务,可以看到如下相关依赖:

拍平的依赖列表无法看不出来代码架构,我们让他立体起来:
暂时无法在飞书文档外展示此内容
-
语义化注解

- 没有业务逻辑,仅仅提供【元信息】。
- 用于在DTO的各个语法位置(类上、字段上、方法上)添加【编解码行为提示】
@JsonProperty
主要作用
- 属性名映射
- 控制 Java 对象字段或方法与 JSON 属性名之间的映射关系
- 可以指定不同于 Java 字段名的 JSON 属性名
- 访问器标记
- 将非静态方法标记为属性的 getter 或 setter
- 将非静态字段标记为可序列化/反序列化的属性
基本用法
字段重命名
public class Person {
@JsonProperty("full_name")
private String name;
@JsonProperty("years_old")
private int age;
}
序列化后 JSON 将是:
{
"full_name": "John",
"years_old": 30
}
访问控制
通过 access() 属性控制序列化/反序列化行为:
Access.READ_ONLY: 只读,仅序列化不反序列化Access.WRITE_ONLY: 只写,仅反序列化不序列化Access.READ_WRITE: 读写,双向支持Access.AUTO: 自动根据可见性规则判断
@JsonFormat
主要作用
- 格式控制:控制各种数据类型的序列化和反序列化格式
- 结构形状定义:指定数据在 JSON 中的表现形式(字符串、数字、对象等)
- 本地化设置:配置时区、地区等本地化信息
- 特性开关:启用或禁用特定的序列化/反序列化特性
基本用法
日期时间格式化
public class Event {
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date eventDate;
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "dd-MM-yyyy hh:mm:ss")
private LocalDateTime localDateTime;
}
数字格式化
public class Product {
@JsonFormat(shape = JsonFormat.Shape.STRING)
private Long id; // 序列化为字符串,避免大数字精度丢失
@JsonFormat(shape = JsonFormat.Shape.NUMBER)
private Boolean active; // 序列化为数字(0或1)
}
枚举处理
// 序列化为字符串
@JsonFormat(shape = JsonFormat.Shape.STRING)
public enum Status {
ACTIVE, INACTIVE
}
// 序列化为数字(索引)
@JsonFormat(shape = JsonFormat.Shape.NUMBER)
public enum Priority {
LOW, MEDIUM, HIGH
}
集合处理
@JsonFormat(shape = JsonFormat.Shape.OBJECT)
public class CustomList extends ArrayList<String> {
// 序列化为对象而不是数组
}
-
解析器/生成器
JSON的编解码的本质是完成【string】<–>【object】的双向转换。
在高级数据结构编解码的中间层,需要一个组件来封装 公共的 JSON格式的【字符】读写能力。
- Parser:JSON字符串的解析、迭代
- Generator:JSON字符串的构造、生成
那么架构就变成了:【string】<–【parser/genetator】–>【object】
这个模式并不特别,都是这么玩的,本质就是 代码分层,各司其职。提升代码可维护性、可拓展性。我们可以对照Hessian2的架构一起看下。
暂时无法在飞书文档外展示此内容
JsonParser解析输入,提供Low Level的【流式】API,供上层ObjectCodec编排。
import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonToken;
import java.io.IOException;
public class JsonParserExample {
public static void main(String[] args) throws IOException {
String jsonString = "{\"name\":\"John\", \"age\":30, \"city\":\"New York\"}";
// 创建 JsonFactory 实例
JsonFactory factory = new JsonFactory();
// 创建 JsonParser 实例
try (JsonParser parser = factory.createParser(jsonString)) {
// 检查当前 token 是否是开始对象
if (parser.nextToken() == JsonToken.START_OBJECT) {
// 遍历对象中的所有字段
while (parser.nextToken() != JsonToken.END_OBJECT) {
String fieldName = parser.getCurrentName();
parser.nextToken(); // 移动到字段值
// 根据字段名处理不同的值
switch (fieldName) {
case "name":
System.out.println("Name: " + parser.getText());
break;
case "age":
System.out.println("Age: " + parser.getIntValue());
break;
case "city":
System.out.println("City: " + parser.getText());
break;
default:
// 跳过未知字段
parser.skipChildren();
break;
}
}
}
}
}
}
- Parser有状态,其持有了底层输入(String/InputStream/File等),每次使用必须要new
- Parser的3个核心API是:nextToken/currentToken/readValueAs。
// 使用 nextToken 遍历并处理 JSON
while (parser.nextToken() != null) {
JsonToken token = parser.getCurrentToken();
switch (token) {
case FIELD_NAME:
String fieldName = parser.getCurrentName();
break;
case VALUE_STRING:
String value = parser.getText();
break;
case VALUE_NUMBER_INT:
int number = parser.getIntValue();
break;
}
}
-
对象映射
-
ObjectCodec


-
对象映射/数据绑定【readValueAs】


-
伪代码
// JsonParser: 负责低级别的JSON解析,逐个读取token
class JsonParser {
// 逐个读取JSON token (START_OBJECT, FIELD_NAME, VALUE_STRING等)
JsonToken nextToken()
// 获取当前token的值
String getText()
int getIntValue()
// ... 其他获取值的方法
// 指向ObjectCodec,用于高级别的对象绑定
ObjectCodec codec
// 使用codec将当前JSON内容绑定到Java对象
T readValueAs(Class<T> type) {
return codec.readValue(this, type)
}
}
// ObjectCodec: 负责高级别的对象序列化/反序列化
class ObjectCodec {
// 使用JsonParser将JSON内容绑定到Java对象
T readValue(JsonParser parser, Class<T> type) {
// 伪代码逻辑:
// 1. 使用parser.nextToken()等方法遍历JSON token
// 2. 根据type信息创建对象实例
// 3. 将JSON值映射到对象属性
// 4. 返回填充好的对象
Object object = createInstance(type)
while (parser.nextToken() != null) {
mapTokenToObjectProperty(parser, object)
}
return object
}
// 使用JsonGenerator将Java对象序列化为JSON
void writeValue(JsonGenerator generator, Object value) {
// 将对象序列化为JSON输出
}
}
// 使用示例:
// 1. 创建parser
JsonFactory factory = new JsonFactory()
JsonParser parser = factory.createParser('{"name":"John", "age":30}')
// 2. 设置codec (如ObjectMapper)
parser.setCodec(new ObjectMapper())
// 3. 使用parser和codec一起工作
Person person = parser.readValueAs(Person.class)
// 内部实际调用: parser.getCodec().readValue(parser, Person.class)
-
插件拓展

ObjectCodec的典型实现是我们日常使用过程中接触最多的ObjectMapper。它承担众多职责之一是【收集自定义配置+拓展】。
拓展的重要一方面是对不同类型/接口的 case by case 的编解码支持,通过 策略模式 实现。
Jackson设计的开闭的拓展发现机制。
常用拓展模块

拓展注册API
使用场景不同,需要的拓展不同。
由【上层使用者】来发现和登记,Jackson本身不做【模块发现】。
体现了Jackson 简单、不僭越 的设计哲学。



-
反序列化器维护



-
插件发现
上层一般配合JDK ServerLoader机制来做【插件发现】。


ObjectMapper










JsonFactory


ReaderBasedJsonParser



nextToken

ParsingContext

压栈

弹栈

JsonDeserializer
简单示例

DTO示例
public class Person {
private String name;
private int age;
private String email;
// 默认构造函数(必需)
public Person() {}
// 带参数的构造函数
public Person(String name, int age, String email) {
this.name = name;
this.age = age;
this.email = email;
}
// Getters and Setters
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public int getAge() { return age; }
public void setAge(int age) { this.age = age; }
public String getEmail() { return email; }
public void setEmail(String email) { this.email = email; }
@Override
public String toString() {
return "Person{name='" + name + "', age=" + age + ", email='" + email + "'}";
}
}
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.IntNode;
import com.fasterxml.jackson.databind.node.TextNode;
import java.io.IOException;
public class PersonDeserializer extends JsonDeserializer<Person> {
@Override
public Person deserialize(JsonParser p, DeserializationContext ctxt)
throws IOException, JsonProcessingException {
// 获取当前节点
JsonNode node = p.getCodec().readTree(p);
// 从 JSON 节点中提取数据
String name = getNodeTextValue(node, "name");
int age = getNodeIntValue(node, "age", 0);
String email = getNodeTextValue(node, "email");
// 创建并返回 Person 对象
Person person = new Person();
person.setName(name);
person.setAge(age);
person.setEmail(email);
return person;
}
// 辅助方法:安全地获取文本值
private String getNodeTextValue(JsonNode node, String fieldName) {
JsonNode fieldNode = node.get(fieldName);
if (fieldNode != null && fieldNode.isTextual()) {
return fieldNode.asText();
}
return null;
}
// 辅助方法:安全地获取整数值
private int getNodeIntValue(JsonNode node, String fieldName, int defaultValue) {
JsonNode fieldNode = node.get(fieldName);
if (fieldNode != null && fieldNode.isNumber()) {
return fieldNode.asInt(defaultValue);
}
return defaultValue;
}
}
总结
Jackson真的太复杂了,看得我脑瓜子嗡嗡的。
