前言
各位好,我是A哥(YourBatman)。上篇文章 整体介绍了世界上最好的JSON库 – Jackson,对它有了整体了解:知晓了它是个生态,其它的仅是个JSON库而已。
有人说Jackson小众?那么请先看看上篇文章吧。学Jackson性价比特别高,因为它使用广泛、会的人少,因此在团队内如果你能精通,附加价值的效应就会非常明显了…
我挠头想了想,本系列来不了虚的,只能肝。本系列教程不仅仅教授基本使用,目标是搞完后能够解决日常99.99%的问题,毕竟每个小团队都最好能有某些方面的小专家,毕竟大家都不乏遇见过一个技术问题卡一天的情况。只有从底层把握,方能游刃有余
命名为core的模块一般都不简单,jackson-core自然也不例外。它是三大核心模块之一,并且是核心中的核心,提供了对JSON数据的完整支持(包括各种读、写)。它是三者中最强大的模块,具有最低的开销和最快的读/写操作。
此模块提供了最具底层的Streaming JSON解析器/生成器,这组流式API属于Low-Level API,具有非常显著的特点:
- 开销小,损耗小,性能极高
- 因为是Low-Level API,所以灵活度极高
- 又因为是Low-Level API,所以易错性高,可读性差
jackson-core模块提供了两种处理JSON的方式(纵缆整个Jackson共三种):
- 流式API:读取并将JSON内容写入作为离散事件 -> JsonParser读取数据,而JsonGenerator负责写入数据
- 树模型:JSON文件在内存里以树形式表示。此种方式也很灵活,它类似于XML的DOM解析,层层嵌套的
作为“底层”技术,应用级开发中确实接触不多。为了引起你的重视,提前预告一下:Spring MVC对JSON消息的转换器AbstractJackson2HttpMessageConverter它就用到了底层流式API -> JsonGenerator写数据。想不想拿下Spring呢?我想你的答案应该是Yes吧~
相信做难事必有所得,你我他都会用的技术、都能解决的问题,那绝成不了你的核心竞争力,自然在团队内就难成发光体。
版本约定
原则:均选当前最新版本(忽略小版本)
- Jackson版本:2.11.0
- Spring Framework版本:5.2.6.RELEASE
- Spring Boot版本:2.3.0.RELEASE
- 内置的Jackson和Spring版本均和👆保持一致,避免了版本交叉
说明:类似2.11.0和2.11.x这种小版本号的差异,你权可认为没有区别
工程结构
鉴于是首次展示工程示例代码,将基本结构展示如下:
全部源码地址在本系列的最后一篇文章中会全部公示出来
正文
Jackson提供了一种对性能有极致要求的方式:流式API。它用于对性能有极致要求的场景,这个时候就可以使用此种方式来对JSON进行读写。
概念解释:流式、增量模式、JsonToken
- 流式(Streaming):此概念和Java8中的Stream流是不同的。这里指的是IO流,因此具有最低的开销和最快的读/写操作(记得关流哦)
- 增量模式(incremental mode):它表示每个部分一个一个地往上增加,类似于垒砖。使用此流式API读写JSON的方式使用的均是增量模式
- JsonToken:每一部分都是一个独立的Token(有不同类型的Token),最终被“拼凑”起来就是一个JSON。这是流式API里很重要的一个抽象概念。
关于增量模式和Token概念,在Spirng的SpEL表达式中也有同样的概念,这在Spring相关专栏里你将会再次体会到
本文将看看它是如何写JSON数据的,也就是JsonGenerator。
JsonGenerator使用Demo
JsonGenerator定义用于编写JSON内容的公共API的基类(抽象类)。实例使用的工厂方法创建,也就是JsonFactory。
小贴士:纵观整个Jackson,它更多的是使用抽象类而非接口,这是它的一大“特色”。因此你熟悉的面向接口编程,到这都要转变为面向抽象类编程喽。
话不多说,先来一个Demo感受一把:
@Test public void test1() throws IOException { JsonFactory factory = new JsonFactory(); // 本处只需演示,向控制台写(当然你可以向文件等任意地方写都是可以的) JsonGenerator jsonGenerator = factory.createGenerator(System.out, JsonEncoding.UTF8); try { jsonGenerator.writeStartObject(); //开始写,也就是这个符号 { jsonGenerator.writeStringField("name", "YourBatman"); jsonGenerator.writeNumberField("age", 18); jsonGenerator.writeEndObject(); //结束写,也就是这个符号 } } finally { jsonGenerator.close(); } }
因为JsonGenerator实现了AutoCloseable
接口,因此可以使用try-with-resources
优雅关闭资源(这也是推荐的使用方式),代码改造如下:
@Test public void test1() throws IOException { JsonFactory factory = new JsonFactory(); // 本处只需演示,向控制台写(当然你可以向文件等任意地方写都是可以的) try (JsonGenerator jsonGenerator = factory.createGenerator(System.out, JsonEncoding.UTF8)) { jsonGenerator.writeStartObject(); //开始写,也就是这个符号 { jsonGenerator.writeStringField("name", "YourBatman"); jsonGenerator.writeNumberField("age", 18); jsonGenerator.writeEndObject(); //结束写,也就是这个符号 } } }
运行程序,控制台输出:
{"name":"YourBatman","age":18}
这是最简使用示例,这也就是所谓的序列化底层实现,从示例中对增量模式能够有所感受吧。
纯手动档有木有,灵活性和性能极高,但易出错。这就像头文字D的赛车一样,先要速度、高性能、灵活性,那必须上手动档
JsonGenerator详细介绍
JsonGenerator是个抽象类,它的继承体系如下:
- WriterBasedJsonGenerator:基于java.io.Writer处理字符编码(话外音:使用Writer输出JSON)
- 因为UTF-8编码基本标准化了,因此Jackson内部也提供了SegmentedStringWriter/UTF8Writer来简化操作
- UTF8JsonGenerator:基于OutputStream + UTF-8处理字符编码(话外音:明确指定了使用UTF-8编码把字节变为字符)
默认情况下(不指定编码),Jackson默认会使用UTF-8进行编码,也就是说会使用UTF8JsonGenerator作为实际的JSON生成器实现类,具体逻辑将在讲述JsonFactory章节中有所体现,敬请关注。
值得注意的是,抽象基类JsonGenerator它只负责JSON的生成,至于把生成好的JSON写到哪里去它并不关心。比如示例中我给写到了控制台,当然你也可以写到文件、写到网络等等。
Spring MVC中的JSON消息转换器就是向HttpOutputMessage(网络输出流)里写JSON数据