前言

JSON是常用的数据编码格式,在从海量JSON格式字符串数据中解析出所需值常常是计算的性能瓶颈,在大数据实时离线场景尤为常见。本文阐述一种高效解析JSON的方案和实现,相比较于jackson,在公司场景应用中,性能平均提升50%+。

基于JAVA的JSON处理源码参考github: https://github.com/lunar-ye/ProtoJson

参考测试用例:

UDF用法参考:

JACKSON瓶颈:

jackson是一种开源主流的json解析工具,详情可以参考:https://github.com/FasterXML/jackson。

jackson常见有两种解析场景,一种为将json解析为JsonNode tree,另一种将json字符串解析为java类

两种解析json的方法逻辑相似,可以分为下面几个部分。

首先,将json字符串进行词法解析,解析成JsonToken组合。

然后,将调用DefaultSerializationContext的readRootValue方法。

最终会递归调用JsonDeserialize方法进解析JsonToken组合。

jackson定义了一系列json反序列化类,不同的反序列化类会将json反序列化为不同的类型。比如MapDeserializer类会将jsontoken集合解析为Map类型,而JsonNode deserializer类会将jsontoken集合解析为JsonNode类型。

但是jackson提供的官方解析方法为了保证易用性(把全量json构建成一棵树,用户按需取),存在会将大量的无用字段递归解析,并且会在json每个路径节点创建不同的对象。

比如:对于json字符串:"{\"a\":1,\"b\":{\"c\":\"xx\",\"d\":[1,2,3],\"e\":[[1,2,3]]}}"

哪怕我们只想解析"a"这个字段的值,当调用jackson的官方解析方法时候(比如readTree),也会将b、b.c、b.d等等字段全部解析出来,并且每个节点构造jsonnode的对象。

优化项:

针对jackson官方解析方案存在的两点问题,分别给出解决方案:

a. 无效字段解析:常见的数据清洗场景,用户需要的字段都是固定的,所以可以只解析需要的字段,不需要的字段可以快速跳过

b. 对象重复创建:将结果存储到节点树上,复用对象,不需要重复创建对象。

以示例JSON为例, a, b, b.c, b.d[1] 解析流程如下

a. 构造节点树,

b. 词法解析json字符串,生成JsonToken集合。

c. 深度遍历JsonToken,赋值节点树,返回结果。

构造了一个简单的case,测试快速json解析方案(protojson)和jackson通用的json解析方案性能。可以自行测试看看。

测试代码可以参考:https://github.com/lunar-ye/ProtoJson/blob/main/Demo/src/test/java/org/protojson/test/JsonRowConverterTest.java

2022-11-01 首发于快手技术博客