有道无术,术尚可求,有术无道,止于术。
本系列Jackson 版本 2.17.0
源码地址:https://gitee.com/pearl-organization/study-seata-demo
文章目录
- 1. 前言
- 2. 解析原理
- 3. 案例演示
- 3.1 创建 JsonParser
- 3.2 解析
- 3.3 读取
- 3.4 测试
1. 前言
在上一篇文档中,我们使用JsonGenerator
生成了一个JSON
文件,接下来我们学习使用JsonParser
进行解析。
2. 解析原理
JsonParser
进行解析的大致流程如下:
- 从头开始扫描
JSON
字符串 - 依次识别每个
JSON
组成元素 - 解析到结束位置,关闭解析器
JSON
格式是非常标准的,JsonParser
会从头到尾识别每一个组成元素,比如识别到对象开始符号{
时,会生成一个对应JsonToken
令牌对象,其中包含了元素标识START_OBJECT
。
JsonToken
对象是一个枚举类,用于判断解析元素的类型:
public enum JsonToken {// 当前无法返回NOT_AVAILABLE((String)null, -1),// 对象开始符号:{START_OBJECT("{", 1),// 对象结束符号:}END_OBJECT("}", 2),// 数组开始符号START_ARRAY("[", 3),// 数组结束符号END_ARRAY("]", 4),// 当时是属性名称FIELD_NAME((String)null, 5),// 嵌入对象VALUE_EMBEDDED_OBJECT((String)null, 12),// 字符串类型值VALUE_STRING((String)null, 6),// INT类型值VALUE_NUMBER_INT((String)null, 7),// FLOAT类型值VALUE_NUMBER_FLOAT((String)null, 8),// True 类型值VALUE_TRUE("true", 9),// False 类型值VALUE_FALSE("false", 10),// null 值VALUE_NULL("null", 11);
}
3. 案例演示
演示需求: 将之前生成的JSON
文件反序列化为User
对象。
3.1 创建 JsonParser
JsonParser
是读取JSON
内容API
的基类,它有很多实现子类:
其实例也是由JsonFactory
创建,JsonFactory
提供了多种创建方法:
public abstract JsonParser createParser(byte[] data) throws IOException;public abstract JsonParser createParser(byte[] data, int offset, int len) throws IOException;public abstract JsonParser createParser(char[] content) throws IOException;public abstract JsonParser createParser(char[] content, int offset, int len) throws IOException;public abstract JsonParser createParser(DataInput in) throws IOException;public abstract JsonParser createParser(File f) throws IOException;public abstract JsonParser createParser(InputStream in) throws IOException;public abstract JsonParser createParser(Reader r) throws IOException;public abstract JsonParser createParser(String content) throws IOException;public abstract JsonParser createParser(URL url) throws IOException;
这里我们直接通过文件对象创建:
// 1. 创建 JsonParserJsonFactory jsonFactory = JsonFactory.builder().build();File file = new File("E:\\TD\\pearl\\study-jackson-demo\\jackson-core-demo\\src\\main\\java\\com\\pearl\\jacksoncore\\demo\\file\\user.json");JsonParser jsonParser = jsonFactory.createParser(file);
上面创建的JsonParser
实例类型是UTF8StreamJsonParser
:
3.2 解析
JsonParser
提供了isClosed()
方法判断是否关闭,nextToken()
方法解析下一个元素,使用这两个方法循环解析JSON
的所有元素:
while (!jsonParser.isClosed()) {JsonToken jsonToken = jsonParser.nextToken();System.out.println("当前解析到的令牌类型:" + jsonToken);}
所有元素类型如下:
当前解析到的令牌类型:START_OBJECT
当前解析到的令牌类型:FIELD_NAME
当前解析到的令牌类型:VALUE_NUMBER_INT
当前解析到的令牌类型:FIELD_NAME
当前解析到的令牌类型:VALUE_STRING
当前解析到的令牌类型:FIELD_NAME
当前解析到的令牌类型:VALUE_NUMBER_INT
当前解析到的令牌类型:FIELD_NAME
当前解析到的令牌类型:START_OBJECT
当前解析到的令牌类型:FIELD_NAME
当前解析到的令牌类型:VALUE_NUMBER_INT
3.3 读取
JsonParser
提供了多个将JSON
解析为对象或树模型的方法:
查看readValueAs
方法,可以看到实际是调用ObjectCodec
对象的读取方法:
public <T> T readValueAs(Class<T> valueType) throws IOException {return this._codec().readValue(this, valueType);}protected ObjectCodec _codec() {ObjectCodec c = this.getCodec();if (c == null) {throw new IllegalStateException("No ObjectCodec defined for parser, needed for deserialization");} else {return c;}}
ObjectCodec
是一个抽象类,定义了JsonParser
和JsonGenerator
用于序列化和反序列化POJO
对象的正则表达式,其标准实现为jackson-databind
模块中的ObjectMapper
类。ObjectCodec
是非常重要的一个类,JSON
和POJO
转换的规则和逻辑需要实现该抽象类。
ObjectCodec
中可以看到定义了很多读写方法,同时它继承了TreeCodec
抽象类,说明也具备读取为树模型的能力:
public abstract class ObjectCodec extends TreeCodec implements Versioned {public abstract Version version();public abstract <T> T readValue(JsonParser var1, Class<T> var2) throws IOException;public abstract <T> T readValue(JsonParser var1, TypeReference<T> var2) throws IOException;public abstract <T> T readValue(JsonParser var1, ResolvedType var2) throws IOException;public abstract <T> Iterator<T> readValues(JsonParser var1, Class<T> var2) throws IOException;public abstract <T> Iterator<T> readValues(JsonParser var1, TypeReference<T> var2) throws IOException;public abstract <T> Iterator<T> readValues(JsonParser var1, ResolvedType var2) throws IOException;public abstract void writeValue(JsonGenerator var1, Object var2) throws IOException;public abstract <T extends TreeNode> T readTree(JsonParser var1) throws IOException;public abstract void writeTree(JsonGenerator var1, TreeNode var2) throws IOException;public abstract TreeNode createObjectNode();public abstract TreeNode createArrayNode();public abstract JsonParser treeAsTokens(TreeNode var1);public abstract <T> T treeToValue(TreeNode var1, Class<T> var2) throws JsonProcessingException;/** @deprecated */@Deprecatedpublic JsonFactory getJsonFactory() {return this.getFactory();}public JsonFactory getFactory() {return this.getJsonFactory();}
}
我们自定义一个StudyObjectCodec
,这里只实现readValue(JsonParser jsonParser, Class<T> aClass)
方法,也只解析了User
的几个属性,因为解析比生成复杂的多,这里只是简单演示:
public class StudyObjectCodec extends ObjectCodec {// 省略其他实现@Overridepublic <T> T readValue(JsonParser jsonParser, Class<T> aClass) throws IOException {// 1. 创建用户对象User user = new User();// 2. 循环解析每一个元素while (!jsonParser.isClosed()) {// 解析到下一个元素JsonToken jsonToken = jsonParser.nextToken();System.out.println("当前解析到的令牌类型:" + jsonToken);// 2.1 如果是属性名称类型if (JsonToken.FIELD_NAME.equals(jsonToken)) {String currentName = jsonParser.currentName(); // 获取属性名System.out.println("属性名称:" + currentName);if ("id".equals(currentName) ) {jsonParser.nextToken(); // 解析到下一个元素,即为属性对应的值Long userId = jsonParser.getLongValue();user.setId(userId);}if ("name".equals(currentName)) {jsonParser.nextToken();String userName = jsonParser.getValueAsString();user.setName(userName);}if ("age".equals(currentName)) {jsonParser.nextToken();int age = jsonParser.getIntValue();user.setAge(age);}}}// 3. 返回return (T) user;}
最后设置ObjectCodec
,完整代码如下:
public class JsonParserDemo {public static void main(String[] args) throws IOException {// 1. 创建 JsonParserJsonFactory jsonFactory = JsonFactory.builder().build();File file = new File("E:\\TD\\pearl\\study-jackson-demo\\jackson-core-demo\\src\\main\\java\\com\\pearl\\jacksoncore\\demo\\file\\user.json");JsonParser jsonParser = jsonFactory.createParser(file);// 2. 反序列化 jsonParser.setCodec(new StudyObjectCodec());User user = jsonParser.readValueAs(User.class);System.out.println(user);}
}
3.4 测试
运行测试案例,查看控制台:
User{id=1701893746586685440, name='坤坤', age=18, org=null, roleList=null}