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

news2024/11/24 14:34:45

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

本系列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进行解析的大致流程如下:

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

在这里插入图片描述
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. 创建 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的所有元素:

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

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

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

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

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,完整代码如下:

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 测试

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

User{id=1701893746586685440, name='坤坤', age=18, org=null, roleList=null}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1532688.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

流畅的 Python 第二版(GPT 重译)(三)

第五章&#xff1a;数据类构建器 数据类就像孩子一样。它们作为一个起点是可以的&#xff0c;但要作为一个成熟的对象参与&#xff0c;它们需要承担一些责任。 马丁福勒和肯特贝克 Python 提供了几种构建简单类的方法&#xff0c;这些类只是一组字段&#xff0c;几乎没有额外功…

隐私计算实训营学习一:数据可信流通,从运维信任到技术信任

文章目录 一、数据可信流通二、数据可信流通的技术信任基础三、技术信任开启数据密态时代&#xff0c;保障广域数据可信流通 一、数据可信流通 可信数据流通体系&#xff1a;数据二十条第一次明确提出可信流通&#xff0c;建立数据来源可确认、使用范围可界定、流通过程可追溯…

【数据可视化】Echarts中的其它图表

个人主页 &#xff1a; zxctscl 如有转载请先通知 文章目录 1. 前言2. 绘制散点图2.1 绘制基本散点图2.2 绘制两个序列的散点图2.3 绘制带涟漪特效的散点图 3. 绘制气泡图3.1 绘制标准气泡图3.2 绘制各国人均寿命与GDP气泡图3.3 绘制城市A、城市B、城市C三个城市空气污染指数气…

智慧公园:AI智能分析网关V4城市公园视频智能监管方案

一、背景分析 随着天气渐渐转暖&#xff0c;城市公园的花卉也逐渐盛开&#xff0c;春暖花开时节&#xff0c;前往公园赏花游玩的城市居民也渐渐多起来&#xff0c;因此安全问题也成为相关监管部门的重要管理任务之一。随着科技的不断进步&#xff0c;智能监控技术已经成为现代…

使用paho.mqtt.client实现MQTT Client连接EMQX Broker

目录 概述 1 认识paho.mqtt.client 2 实现MQTT Client 2.1 功能介绍 2.2 paho.mqtt.client库函数介绍 2.3 MQTT Client实现 2.3.1 创建项目 2.3.2 编写MQTT Client代码 2.3.3 Log工具源码 2.4 功能测试代码实现 2.4.1 功能介绍 2.4.2 代码实现 3 测试 3.1 EMQX上创…

回归预测 | Matlab基于SAO-LSTM雪消融算法优化长短期记忆神经网络的数据多输入单输出回归预测

回归预测 | Matlab基于SAO-LSTM雪消融算法优化长短期记忆神经网络的数据多输入单输出回归预测 目录 回归预测 | Matlab基于SAO-LSTM雪消融算法优化长短期记忆神经网络的数据多输入单输出回归预测效果一览基本介绍程序设计参考资料 效果一览 基本介绍 1.Matlab基于SAO-LSTM雪消融…

课时70:流程控制_for循环_嵌套循环

2.4.4 嵌套循环 学习目标 这一节&#xff0c;我们从 基础知识、简单实践、小结 三个方面来学习。 基础知识 简介 这里的嵌套实践&#xff0c;与选择语句的嵌套实践基本一致&#xff0c;只不过组合的方式发生了一些变化。常见的组合样式如下&#xff1a;for嵌套for语句for …

[STM32] Keil MDK 新建工程编译不通过(warning: #2803-D和Error: L6218E)解决方法备忘

按照野火的PDF教程的第4章&#xff1a;[野火]《RT-Thread 内核实现与应用开发实战—基于STM32》.pdf 新建 Keil MDK 工程&#xff0c;工程设置完成后点击编译按钮&#xff0c;编译不通过&#xff1a; RTE\Device\ARMCM3\startup_ARMCM3.c(75): warning: #2803-D: unrecognize…

nodejs社区垃圾分类管理平台的设计与实现python-flask-django-php

近些年来&#xff0c;随着科技的飞速发展&#xff0c;互联网的普及逐渐延伸到各行各业中&#xff0c;给人们生活带来了十分的便利&#xff0c;社区垃圾分类管理平台利用计算机网络实现信息化管理&#xff0c;使整个社区垃圾分类管理的发展和服务水平有显著提升。 语言&#xf…

Hash类型

2.3.Hash类型 Hash类型&#xff0c;也叫散列&#xff0c;其value是一个无序字典&#xff0c;类似于Java中的HashMap结构。 String结构是将对象序列化为JSON字符串后存储&#xff0c;当需要修改对象某个字段时很不方便&#xff1a; Hash结构可以将对象中的每个字段独立存储&am…

【Qt】使用Qt实现Web服务器(三):QtWebApp中HttpRequest和HttpResponse

1、HttpRequest 1.1 示例 1)在Demo1的Dump HTTP request示例 在浏览器中输入http://127.0.0.1:8080点击Dump HTTP request 2)切换到页面:http://127.0.0.1:8080/dump 该页面显示请求和响应的内容: Request: Method: GET Path: /dump Version: HTTP/1.1 Headers: accep…

图片编辑器中实现文件上传的三种方式和二进制流及文件头校验文件类型

背景 最近在 vue-design-editor 开源项目中实现 psd 等多种文件格式上传解析成模板过程中, 发现搞定设计文件上传没有使用 input 实现文件上传, 所以我研究了一下相关技术, 总结了以下三种文件上传方法 input 文件选择window.showOpenFilePicker 和 window.showDirectoryPicke…

树莓派夜视摄像头拍摄红外LED灯

NoIR相机是一种特殊类型的红外摄像头&#xff0c;其名称来源于"No Infrared"的缩写。与普通的彩色摄像头不同&#xff0c;NoIR相机具备红外摄影和低光条件下摄影的能力。 一般摄像头能够感知可见光&#xff0c;并用于普通摄影和视频拍摄。而NoIR相机则在设计上去除了…

【Hadoop】Hadoop 编译源码

目录 为什么要源码编译Hadoop 编译源码1前期工作准备2jar 包安装2.1安装 Maven2.2安装 ant2.3安装 glibc-headers 和 g2.4安装 make 和 cmake2.5安装 protobuf2.6安装 openssl 库2.7安装 ncurses-devel 库 3编译源码3.1解压源码到 /opt/ 目录3.2 进入到 hadoop 源码主目录 /opt…

Redis持久化笔记(3)

redis持久化&#xff1a;把内存的数据存放到磁盘&#xff0c;避免因为断电等导致数据丢失。 RDB&#xff08;Redis Database&#xff09; rdb就是在一定时间间隔内把当时的数据和状态保存为 .rdb文件放在磁盘中。 自动触发设置&#xff1a;在redis.conf 修改.rdb文件的保存位…

【Vue】三、使用ElementUI实现图片上传

目录 一、前端代码实现 二、后端代码实现 三、调试效果实现 一、前端代码实现 废话不多说直接上代码 <el-form-item prop"image" label"上传图片" v-model"form.image"><el-upload:action"http://localhost:8…

【prometheus-operator】k8s监控集群外redis

1、部署exporter GitHub - oliver006/redis_exporter: Prometheus Exporter for Redis Metrics. Supports Redis 2.x, 3.x, 4.x, 5.x, 6.x, and 7.x redis_exporter-v1.57.0.linux-386.tar.gz # 解压 tar -zxvf redis_exporter-v1.57.0.linux-386.tar.gz # 启动 nohup ./redi…

Go——指针和内存逃逸

区别于C/C中的指针&#xff0c;Go语言中的指针不能进行偏移和运算&#xff0c;是安全指针。 要搞明白Go语言中的指针概念需要先知道3个概念&#xff1a;指针地址&#xff0c;指针类型和指针取值。 一. Go语言的指针 Go语言中的函数传参都是值拷贝&#xff0c;当我们想修改某个…

# Django通过开关控制数据库参数(JS版)

目录 场景初始的视图层HTML部分JS代码视图层接受部分 场景 此时我的表单中有一排开关 数据库有一排状态 需求是要当开关开启时数据库state为1&#xff0c;关闭时为0 初始的视图层 将整个adv数据表返回给前端HTML def adv(request):adv_list Adv.objects.all()return rende…

语言教育App头牌Duolingo如何重新点燃用户增长350%?

Duolingo是全球最大的语言教育APP&#xff0c;拥有数亿用户&#xff0c;然而用户增长正在放缓&#xff0c;本案例以Duolingo增长 通过数据建模洞察关键指标&#xff0c;并围绕指标用增长实验驱动&#xff0c;设计植根于创新的增长模式&#xff0c;包括启动排行榜&#xff0c;重…