Jackson 2.x 系列【3】解析器 JsonParser

有道无术,术尚可求,有术无道,止于术。

本系列Jackson 版本 2.17.0

源码地址:https://gitee.com/pearl-organization/study-seata-demo

文章目录

    • [1. 前言](#1. 前言)
    • [2. 解析原理](#2. 解析原理)
    • [3. 案例演示](#3. 案例演示)
      • [3.1 创建 JsonParser](#3.1 创建 JsonParser)
      • [3.2 解析](#3.2 解析)
      • [3.3 读取](#3.3 读取)
      • [3.4 测试](#3.4 测试)

1. 前言

在上一篇文档中,我们使用JsonGenerator生成了一个JSON文件,接下来我们学习使用JsonParser进行解析。

2. 解析原理

JsonParser进行解析的大致流程如下:

  1. 从头开始扫描JSON字符串
  2. 依次识别每个JSON组成元素
  3. 解析到结束位置,关闭解析器


JSON格式是非常标准的,JsonParser会从头到尾识别每一个组成元素,比如识别到对象开始符号{时,会生成一个对应JsonToken令牌对象,其中包含了元素标识START_OBJECT

JsonToken对象是一个枚举类,用于判断解析元素的类型:

java 复制代码
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提供了多种创建方法:

java 复制代码
    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;

这里我们直接通过文件对象创建:

java 复制代码
        // 1. 创建 JsonParser
        JsonFactory 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的所有元素:

java 复制代码
        while (!jsonParser.isClosed()) {
            JsonToken jsonToken = jsonParser.nextToken();
            System.out.println("当前解析到的令牌类型:" + jsonToken);
        }

所有元素类型如下:

java 复制代码
当前解析到的令牌类型: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对象的读取方法:

java 复制代码
    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是一个抽象类,定义了JsonParserJsonGenerator用于序列化和反序列化POJO对象的正则表达式,其标准实现为jackson-databind模块中的ObjectMapper类。ObjectCodec是非常重要的一个类,JSONPOJO转换的规则和逻辑需要实现该抽象类。

ObjectCodec中可以看到定义了很多读写方法,同时它继承了TreeCodec抽象类,说明也具备读取为树模型的能力:

java 复制代码
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 */
    @Deprecated
    public JsonFactory getJsonFactory() {
        return this.getFactory();
    }

    public JsonFactory getFactory() {
        return this.getJsonFactory();
    }
}

我们自定义一个StudyObjectCodec,这里只实现readValue(JsonParser jsonParser, Class<T> aClass)方法,也只解析了User的几个属性,因为解析比生成复杂的多,这里只是简单演示:

java 复制代码
public class StudyObjectCodec extends ObjectCodec {
 	
 	// 省略其他实现
 
    @Override
    public <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,完整代码如下:

java 复制代码
public class JsonParserDemo {
    public static void main(String[] args) throws IOException {
        // 1. 创建 JsonParser
        JsonFactory 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 测试

运行测试案例,查看控制台:

java 复制代码
User{id=1701893746586685440, name='坤坤', age=18, org=null, roleList=null}
相关推荐
ExiFengs36 分钟前
实际项目Java1.8流处理, Optional常见用法
java·开发语言·spring
瓜牛_gn1 小时前
依赖注入注解
java·后端·spring
一元咖啡2 小时前
SpringCloud Gateway转发请求到同一个服务的不同端口
spring·spring cloud·gateway
java亮小白19974 小时前
Spring循环依赖如何解决的?
java·后端·spring
苏-言4 小时前
Spring IOC实战指南:从零到一的构建过程
java·数据库·spring
草莓base4 小时前
【手写一个spring】spring源码的简单实现--容器启动
java·后端·spring
冰帝海岸11 小时前
01-spring security认证笔记
java·笔记·spring
没书读了12 小时前
ssm框架-spring-spring声明式事务
java·数据库·spring
代码小鑫15 小时前
A043-基于Spring Boot的秒杀系统设计与实现
java·开发语言·数据库·spring boot·后端·spring·毕业设计
真心喜欢你吖15 小时前
SpringBoot与MongoDB深度整合及应用案例
java·spring boot·后端·mongodb·spring