如何使用PySpark炸开存储为String类型的DataFrame数组列?
问题原因及解决方案
你的special_values列字符串不是标准JSON格式,这是导致from_json解析失败、最终得到空DataFrame的核心原因:
- 标准JSON要求键名必须用双引号包裹,比如
{"name":"address"},但你的字符串里是{name=address},键名没有引号 - 标准JSON用冒号
:分隔键值对,你的用的是等号= - 字符串类型的取值也没有双引号,比如
value=some address应该是"value":"some address"
from_json只能解析标准JSON格式的字符串,所以解析后special_value列全是null,后续inline自然没有数据输出。
解决方案
先通过字符串替换把非标准格式转换成标准JSON,再进行解析和炸开。具体代码如下:
from pyspark.sql import functions as F from pyspark.sql.types import ArrayType, StructType, StructField, StringType # 定义目标Schema user_schema = ArrayType( StructType([ StructField("name", StringType(), True), StructField("value", StringType(), True) ]) ) df2 = (df1 # 替换键名后的等号为冒号,并给键名加双引号 .withColumn("json_str", F.regexp_replace("special_values", r"(\w+)=", r'"\1":')) # 给无引号的字符串值加上双引号 .withColumn("json_str", F.regexp_replace("json_str", r":([^,\}]+)", r':"$1"')) # 解析成数组结构 .withColumn("special_value", F.from_json("json_str", user_schema)) # 炸开数组并保留原有列,重命名避免字段冲突 .selectExpr("name", "last_name", "inline(special_value)") .withColumnRenamed("name", "attr_name") ) df2.show()
执行后会得到预期结果:
+----+---------+---------+-------------+ |name|last_name|attr_name| value| +----+---------+---------+-------------+ | A| B| address| some address| | A| B| city| Chd| | A| B| zip_code| 160036| | X| Y| address| some address| | X| Y| city| Dallas| | X| Y| zip_code| 02431| +----+---------+---------+-------------+
额外优化建议
如果是从你提供的源JSON数据加载DataFrame,建议直接指定完整Schema加载,避免把数组转成String类型:
# 定义完整Schema full_schema = StructType([ StructField("name", StringType(), True), StructField("last_name", StringType(), True), StructField("special_values", user_schema, True) ]) # 直接加载JSON数据(假设数据存于文件) df1 = spark.read.schema(full_schema).json("path/to/your/data.json") # 直接炸开数组即可 df2 = df1.selectExpr("name", "last_name", "inline(special_values)")
内容的提问来源于stack exchange,提问作者AB21
相关产品推荐
相关产品推荐

