Spark采坑记录(一):json格式字符串转化为复杂数据结构
前言
目前公司的批开发框架基于spark, 流式处理基于structure streaming和spark sql。
目前整体开发流程与大部分流式开发平台想法一致,将数据的ETL操作分割成独立功能的operator,其中采坑的部分与SqlOperator有关。
顾名思义,此operator的作用是将流数据映射成动态表,使用sql的方式进行操作。
主要技术框架背景介绍
spark : 2.4.3
scala : 2.11.12
问题演示过程
mock 数据
{"id":10000000,"json_str":"[{\"uid\":\"1000001\",\"name\":\"test-name-1\",\"type\":\"test-type-1\",\"kw_idxs\":[[[0, 0]]],\"text_idxs\":[[0, 0]]},{\"id\":\"1000002\",\"name\":\"test-name-2\",\"type\":\"test-type-2\",\"kw_idxs\":[[[0, 0]]],\"text_idxs\":[[0, 0]]}]"}
操作过程
structure streaming 消费kafka topic的数据,配合schema register,读取数据,上文为mock的测试数据。
可以看出,json_str 数据类型为json array格式的string类型。
目标是抽取json_str的id字段,组成所有id字段组成的数组。
所以需要先将json字符串转化成json和struct结构类型方便下一步操作。
查看spark sql 对应版本json相关函数,注意到schema_of_json函数。
https://spark.apache.org/docs/2.4.3/api/sql/
查看文档突然眼前一亮,看起来这个函数可以完美解决我的需求。动手测试!
将数据放入json文件中,使用 spark.read.json的方式进行读取,结果如下:
这个就很奇怪了,查阅了很多文档,大多数都是在将DF操作数据流的,没有针对于spark sql中此函数更好的解释。
所以另外做了一个测试,将mock数据直接放入函数中,不在文件中读取。
结果居然可以正常解析??数据是相同的,为什么会这样呢?
查阅相关文档发现,其实这个是有人提过相关问题:
https://github.com/apache/spark/pull/22775
然而2.4.3的版本看来并没有修复此问题。
目前还未验证3.0是否也存在相同问题。
解决方法
使用from_json方法将json格式数据转化为结构化数据类型。
可以使用schema_of_json方法先解析静态数据,可以在console中看到相应的数据结构类型,在此示例中为:
array<struct<id:string,ks:array<array<array<bigint>>>,name:string,ts:array<array<bigint>>,type:string,uid:string>>
然后使用from_json函数进行解析:
1 select 2 from_json(json_str,'array<struct<id:string,ks:array<array<array<bigint>>>,name:string,ts:array<array<bigint>>,type:string,uid:string>>') as json_obj 3 from 4 test
这样就可以将任意json复杂结构转化为结构化数据结构。
总结
不得不吐槽下spark sql的官方文档,过于简洁,没有更多的test case,导致花了很多时间在踩坑。
例如from_json example :
> SELECT from_json('{"a":1, "b":0.8}', 'a INT, b DOUBLE'); {"a":1,"b":0.8} > SELECT from_json('{"time":"26/08/2015"}', 'time Timestamp', map('timestampFormat', 'dd/MM/yyyy')); {"time":2015-08-26 00:00:00}
其实在复杂数据结构中,数据类型定义会存在有冒号的情况,但是文档中并没有给出可能存在的情况,使用schema_of_json的方式获取结构也是没办法确定正确格式到底是什么才使用的方法,在新上手时,如果不使用schema_of_json方法获取的话,手写很难写对。