手写JSON MiniFastJson
FastJSON 本身是 Java JSON 库,核心能力是 Java 对象和 JSON 字符串之间的序列化、反序列化;官方仓库也提示 FastJSON 2.x 已发布并推荐升级。
一、项目目标
项目定位
用 JavaSE 手写一个简化版 JSON 解析工具,实现:
JsonObject obj = ZXJson.parseObject(json);
String name = obj.getString("name");
User user = ZXJson.parseObject(json, User.class);
String jsonStr = ZXJson.toJSONString(user);
- JSON 字符串是如何被拆成 Token 的;
- Token 是如何被解析成对象树的;
- 嵌套对象、数组、字符串、数字、布尔值、null 是如何表示的;
- JavaBean 如何和 JSON 互相转换;
- FastJSON、Jackson、Gson 这类工具底层大概怎么工作。
二、复杂 JSON 示例
{
"code": 200,
"message": "success",
"data": {
"user": {
"id": 1001,
"name": "周鑫",
"active": true,
"roles": ["admin", "teacher", "student"],
"profile": {
"age": 22,
"email": "test@example.com",
"score": 98.5
}
},
"courses": [
{
"id": 1,
"name": "JavaSE",
"tags": ["基础", "面向对象", "集合"]
},
{
"id": 2,
"name": "Spring Boot",
"tags": ["后端", "项目实战"]
}
]
},
"error": null
}
这个 JSON 覆盖了:
三、整体技术路线
手写 JSON 解析器建议分成两个核心阶段:
JSON 字符串
↓
词法分析 Lexer / Tokenizer
↓
Token 列表
↓
语法分析 Parser
↓
JsonObject / JsonArray / JsonValue
↓
JavaBean 映射 / 序列化输出
你给的第一篇参考文章也采用这个思路:JSON 解析过程通常包括词法分析和语法分析,词法分析把字符串拆成 Token,语法分析再检查 Token 是否符合 JSON 文法。
四、项目阶段规划
阶段一:认识 JSON 结构
目标
让学生先不写代码,先理解 JSON 的基本语法。
要掌握的内容
JSON 支持的基本元素:
对象:{}
数组:[]
字符串:"hello"
数字:123 / 12.5 / -10
布尔值:true / false
空值:null
分隔符:: ,
JSON 对象本质上是:
key-value 键值对
JSON 数组本质上是:
多个 value 的有序集合
阶段产出
写出项目语法规则文档:
object = { } | { members }
members = pair | pair , members
pair = string : value
array = [ ] | [ elements ]
elements = value | value , elements
value = string | number | object | array | true | false | null
阶段二:实现 Token 类型
目标
先定义 JSON 中会出现的所有 Token。
TokenType 枚举设计
public enum TokenType {
LEFT_BRACE, // {
RIGHT_BRACE, // }
LEFT_BRACKET, // [
RIGHT_BRACKET, // ]
COLON, // :
COMMA, // ,
STRING, // "hello"
NUMBER, // 123 / 12.5
TRUE, // true
FALSE, // false
NULL, // null
EOF // 结束
}
Token 类设计
public class Token {
private TokenType type;
private String value;
private int line;
private int column;
}
为什么要有 line 和 column?
为了报错更清楚,比如:
JSON 解析错误:第 3 行,第 15 列,期望 ':',实际遇到 ','
这比单纯抛出:
Parse error
教学效果更好。
阶段三:实现词法分析器 Lexer
目标
把 JSON 字符串转换成 Token 列表。
示例
输入:
{"name":"周鑫","age":22}
输出:
LEFT_BRACE {
STRING name
COLON :
STRING 周鑫
COMMA ,
STRING age
COLON :
NUMBER 22
RIGHT_BRACE }
EOF
Lexer 要实现的功能
重点方法
public List<Token> tokenize(String json);
private Token readString();
private Token readNumber();
private Token readTrue();
private Token readFalse();
private Token readNull();
阶段验收
能够把下面 JSON 正确拆分成 Token:
{
"name": "周鑫",
"age": 22,
"active": true,
"tags": ["Java", "JSON"]
}
阶段四:实现 JSON 数据模型
目标
不要一上来就转 JavaBean,先设计自己的 JSON 数据结构。
类结构设计
JsonValue
├── JsonObject
├── JsonArray
├── JsonString
├── JsonNumber
├── JsonBoolean
└── JsonNull
JsonObject
public class JsonObject extends JsonValue {
private Map<String, JsonValue> values = new LinkedHashMap<>();
public JsonValue get(String key);
public String getString(String key);
public Integer getInteger(String key);
public Double getDouble(String key);
public Boolean getBoolean(String key);
public JsonObject getObject(String key);
public JsonArray getArray(String key);
}
建议用 LinkedHashMap,因为它可以保留 JSON 字段原始顺序。
JsonArray
public class JsonArray extends JsonValue {
private List<JsonValue> values = new ArrayList<>();
public JsonValue get(int index);
public String getString(int index);
public JsonObject getObject(int index);
public int size();
}
阶段五:实现语法分析器 Parser
目标
把 Token 列表转换成 JsonObject / JsonArray。
核心思想
使用递归下降解析。
parseValue()
如果当前 Token 是 {
parseObject()
如果当前 Token 是 [
parseArray()
如果当前 Token 是 STRING
parseString()
如果当前 Token 是 NUMBER
parseNumber()
如果当前 Token 是 TRUE / FALSE
parseBoolean()
如果当前 Token 是 NULL
parseNull()
parseObject 逻辑
1. 读取 {
2. 判断是否直接遇到 }
3. 读取 key,必须是字符串
4. 读取 :
5. 读取 value
6. 如果遇到 ,,继续读取下一个 key-value
7. 如果遇到 },对象结束
parseArray 逻辑
1. 读取 [
2. 判断是否直接遇到 ]
3. 读取 value
4. 如果遇到 ,,继续读取下一个 value
5. 如果遇到 ],数组结束
示例解析过程
输入:
{"user":{"name":"周鑫","age":22}}
解析过程:
parseObject
key = user
value = parseObject
key = name
value = JsonString("周鑫")
key = age
value = JsonNumber(22)
阶段验收
支持下面这种多层嵌套:
JsonObject obj = ZXJson.parseObject(json);
String name = obj
.getObject("data")
.getObject("user")
.getString("name");
阶段六:实现 JavaBean 映射
目标
实现类似 FastJSON 的基础能力:
User user = ZXJson.parseObject(json, User.class);
第二篇参考文章中的 JsonUtils 也围绕 toJson(Object)、fromJson(String, Class<T>)、toMap(...)、格式化 JSON 等工具方法展开,这可以作为你后续 API 设计的参考。(CSDN博客)
需要用到的 JavaSE 技术
示例 JavaBean
public class User {
private Integer id;
private String name;
private Boolean active;
}
映射逻辑
1. JSON 字符串解析成 JsonObject
2. 通过 clazz.getDeclaredConstructor() 创建对象
3. 遍历 clazz.getDeclaredFields()
4. 根据字段名从 JsonObject 中取值
5. 根据字段类型进行转换
6. field.setAccessible(true)
7. field.set(obj, value)
8. 返回 JavaBean
支持字段类型
第一版只支持:
String
Integer / int
Long / long
Double / double
Boolean / boolean
自定义对象
List<String>
List<Integer>
List<自定义对象>
阶段七:实现对象转 JSON 字符串
目标
实现:
String json = ZXJson.toJSONString(user);
支持类型
示例
Java 对象:
User user = new User();
user.setId(1);
user.setName("周鑫");
user.setActive(true);
输出:
{"id":1,"name":"周鑫","active":true}
阶段八:实现注解增强
目标
模仿 FastJSON / Jackson 的字段控制能力。
自定义注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface JsonField {
String name() default "";
boolean serialize() default true;
boolean deserialize() default true;
}
示例
public class User {
@JsonField(name = "user_name")
private String name;
@JsonField(serialize = false)
private String password;
}
效果
{
"user_name": "周鑫"
}
阶段九:实现格式化输出
目标
实现:
String prettyJson = ZXJson.toPrettyJSONString(obj);
普通输出
{"id":1,"name":"周鑫","roles":["admin","teacher"]}
格式化输出
{
"id": 1,
"name": "周鑫",
"roles": [
"admin",
"teacher"
]
}
可实现功能
五、推荐项目结构
zx-json
├── src
│ └── main
│ └── java
│ └── com.zx.json
│ ├── ZXJson.java
│ ├── lexer
│ │ ├── JsonLexer.java
│ │ ├── Token.java
│ │ └── TokenType.java
│ ├── parser
│ │ └── JsonParser.java
│ ├── model
│ │ ├── JsonValue.java
│ │ ├── JsonObject.java
│ │ ├── JsonArray.java
│ │ ├── JsonString.java
│ │ ├── JsonNumber.java
│ │ ├── JsonBoolean.java
│ │ └── JsonNull.java
│ ├── serializer
│ │ └── JsonSerializer.java
│ ├── mapper
│ │ └── BeanMapper.java
│ ├── annotation
│ │ └── JsonField.java
│ └── exception
│ ├── JsonParseException.java
│ └── JsonMappingException.java
└── test
└── java
└── com.zx.json
├── LexerTest.java
├── ParserTest.java
├── BeanMapperTest.java
└── SerializerTest.java
六、核心 API 设计
门面类 ZXJson
public class ZXJson {
public static JsonValue parse(String json);
public static JsonObject parseObject(String json);
public static JsonArray parseArray(String json);
public static <T> T parseObject(String json, Class<T> clazz);
public static String toJSONString(Object object);
public static String toPrettyJSONString(Object object);
}
使用示例
String json = "{\"name\":\"周鑫\",\"age\":22}";
JsonObject obj = ZXJson.parseObject(json);
System.out.println(obj.getString("name"));
System.out.println(obj.getInteger("age"));
JavaBean 示例
User user = ZXJson.parseObject(json, User.class);
System.out.println(user.getName());
七、阶段任务安排
第 1 阶段:JSON 基础与 Token 设计
任务
- 学习 JSON 基本语法;
- 设计
TokenType; - 设计
Token; - 手动分析几个 JSON 示例的 Token 序列。
交付物
TokenType.java
Token.java
JSON语法说明.md
第 2 阶段:词法分析器 Lexer
任务
- 实现字符读取;
- 实现字符串识别;
- 实现数字识别;
- 实现 true / false / null 识别;
- 实现非法字符报错。
交付物
JsonLexer.java
LexerTest.java
验收标准
输入:
{"name":"周鑫","age":22}
可以输出 Token 列表。
第 3 阶段:JsonObject 和 JsonArray
任务
- 设计
JsonValue抽象类; - 实现
JsonObject; - 实现
JsonArray; - 实现基础 getter 方法。
交付物
JsonValue.java
JsonObject.java
JsonArray.java
JsonString.java
JsonNumber.java
JsonBoolean.java
JsonNull.java
第 4 阶段:语法分析器 Parser
任务
- 实现
parseValue(); - 实现
parseObject(); - 实现
parseArray(); - 支持对象和数组递归嵌套;
- 支持错误提示。
交付物
JsonParser.java
ParserTest.java
验收标准
支持:
{
"user": {
"name": "周鑫",
"roles": ["admin", "teacher"]
}
}
第 5 阶段:JavaBean 反序列化
任务
- 通过反射创建对象;
- 通过字段名匹配 JSON key;
- 支持基础类型转换;
- 支持嵌套对象;
- 支持 List 集合。
交付物
BeanMapper.java
JsonMappingException.java
BeanMapperTest.java
第 6 阶段:对象序列化
任务
- 支持基础类型转 JSON;
- 支持 JavaBean 转 JSON;
- 支持 List / Map 转 JSON;
- 支持数组转 JSON;
- 处理字符串转义。
交付物
JsonSerializer.java
SerializerTest.java
第 7 阶段:注解和格式化增强
任务
- 实现
@JsonField; - 支持字段别名;
- 支持忽略字段;
- 支持格式化输出;
- 支持紧凑输出。
交付物
JsonField.java
PrettyPrinter.java
第 8 阶段:项目完善与文档
任务
- 编写 README;
- 编写使用示例;
- 补充异常测试;
- 补充复杂 JSON 测试;
- 和 FastJSON 做 API 对比。
交付物
README.md
使用说明.md
测试报告.md
八、功能清单
基础功能
进阶功能
九、学生最终可以学到什么
这个项目非常适合 JavaSE 教学,因为它能串联很多基础知识:
十一、项目难点
字符串转义
比如:
{
"text": "hello \"world\""
}
需要正确识别:
\"
\\
\n
\r
\t
\u4e2d
数字解析
需要支持:
123
-123
3.14
-3.14
1e10
1.5E-3
第一版可以先只支持整数和小数,科学计数法放到增强阶段。
嵌套递归
比如:
{
"a": {
"b": {
"c": [1, 2, {"d": true}]
}
}
}
这类结构必须靠递归解析。
类型转换
JSON 中只有 number,但 Java 里有:
Integer
Long
Double
Float
BigDecimal
第一版建议:
整数默认 Integer / Long
小数默认 Double
后期再加 BigDecimal。
十二、最终验收标准
项目完成后,至少要支持这些调用:
String json = "{ \"name\": \"周鑫\", \"age\": 22 }";
JsonObject obj = ZXJson.parseObject(json);
System.out.println(obj.getString("name"));
System.out.println(obj.getInteger("age"));
User user = ZXJson.parseObject(json, User.class);
String jsonStr = ZXJson.toJSONString(user);
String pretty = ZXJson.toPrettyJSONString(user);