Spark使用from_json解析JSON后如何移除null值或直接提取字段值
问题根因
之前的解析逻辑之所以出问题,核心是schema推导方式不对:
- 用
spark.read.json扫描全量JSON文本推导schema时,会把所有行出现过的顶层键(也就是本例里的a、b、c、d四个完全不重叠的键)合并成一个包含4个字段的StructType结构 from_json解析时严格按照这个合并后的Struct结构逐行匹配,凡是Struct里存在但当前行JSON没有的键,都会自动填充null占位,最终才会输出类似[hello,,,]这种带空位置的结构- 你实际需要的是每行JSON里那个唯一顶层键对应的原始值,不管这个值是字符串、数组还是嵌套对象,把它转成字符串就行,根本不需要套用把所有键凑到一起的Struct结构。
实现方案
因为你已经明确了:不同行的JSON顶层键完全不重叠,每行只有1个顶层键,完全没必要提前推导全局合并schema,用灵活的Map结构逐行解析就可以做到无null占位,而且最终输出直接是string类型。
代码实现
from pyspark.sql.functions import from_json, map_values, element_at, col from pyspark.sql.types import MapType, StringType # 定义Map格式的解析schema:键为字符串,值统一按字符串存储任意嵌套的JSON内容 map_schema = MapType(StringType(), StringType()) df_result = ( df # 把原来的jsonData列逐行解析为Map结构,每行的Map仅包含自身JSON里的那一个键值对 .withColumn("json_map", from_json(col("jsonData"), map_schema)) # 取出Map里唯一的value,作为最终的jsonParsedData列 .withColumn("jsonParsedData", element_at(map_values(col("json_map")), 1)) # 删掉临时用的中间列 .drop("json_map") )
实际输出
执行df_result.show(truncate=False)就能得到你预期的效果:
+---+----------------------------------------+------------------------------------+ |id |jsonData |jsonParsedData | +---+----------------------------------------+------------------------------------+ |1 |{"a": "hello"} |"hello" | |2 |{"b": ["foo", "bar"]} |["foo","bar"] | |3 |{"c": {"cc": "baz"}} |{"cc":"baz"} | |4 |{"d": [{"dd": "foo"}, {"dd": "bar"}]} |[{"dd":"foo"},{"dd":"bar"}] | +---+----------------------------------------+------------------------------------+
方案说明
- 不会出现跨行键合并带来的null占位:因为全程没有把所有行的键强行拼到一个Struct结构里,每行的Map只保留自己JSON本身带的键,别的行的键根本不会被拉进来
- 最终输出列本身就是string类型,不需要额外做类型转换
- 兼容任意格式的JSON值:不管值是纯字符串、数组、嵌套对象还是对象数组,都能正确提取
兼容低版本Spark的替代方案:如果用的Spark版本不支持Map类型灵活解析嵌套JSON,可以直接用正则抠内容——写正则表达式
^\{".*?":(.*)\}$匹配jsonData列,提取第一个捕获组的内容就是目标值,这种方式完全不依赖schema推导,同样不会产生null占位。
内容的提问来源于stack exchange,提问作者lemon
相关产品推荐
相关产品推荐

