首页 > 解决方案 > Spark Select 对不同对象的 JSON 数组进行选择

问题描述

在 Databricks/Spark/Python(使用 pyspark 的 Spark 版本 2.4.0)中,我从 MongoDB 中获取了一个集合,其中包含一个可以嵌套的不同对象数组。我想将其转换为我可以选择的某种架构/结构。

我尝试了许多不同的方法,但找不到将其转换为模式/结构的优雅方法。

简化的 JSON:

{ 
    "id" : "abc123", 
    "parent" : [
        {
            "field1" : "1"
        },
        {
            "field1" : "11"
        }, 
        {
            "field2" : "2", 
            "field3" : {
                "field3a" : "3a", 
                "field3b" : "3b"
            }, 
        }, 
        {
            "field4" : "4", 
            "field5" : "5",
        },
        {
            "field4" : "44", 
            "field5" : "55",
        }
    ]
}

父级下的对象在父级之间可能不同,因此为所有情况定义特定模式过于复杂。另请注意,对于父项,字段可能会出现多次。

方法 1:自动模式。使用 spark.read.format("com.mongodb.spark.sql.DefaultSource") 会产生一个父字段,该字段混合了具有大量空值的所有字段。

方法 2: JSON 函数。Databricks 有一篇关于转换复杂数据类型的好文章。它读起来像 struct("*") 或 json_tuple 或其他函数可以在这里使用,但我找不到任何成功的组合。

方法 3:动态模式。使用此模式有些成功,但不处理嵌套字段,并且还强制所有字段值都为字符串。

schema = (StructType()
  .add("id", StringType())
  .add("parent", StringType())
)

df = get_my_mongdb_collection_with_schema_function(..., schema)

parent_schema = ArrayType(
    MapType(StringType(), StringType())
)

df = df.withColumn('parent', from_json(df['parent'], parent_schema))

标签: apache-sparkpyspark-sql

解决方案


get_json_object 函数通常可以实现这里所需要的。如果支持所有 JSONPath 运算符,那将是理想的。但是,似乎只支持以下运算符(但很难确认)。

$ Root object
. Child operator
[] Subscript operator for array
* Wildcard for []

读取数据时,指定模式强制包含json的列输入字符串。

schema = (StructType()
  .add("id", StringType())
  .add("parent", StringType())
)

我能够使用 withColumn 将列添加到数据框中,或者只是将其用作选择的一部分。例如

df = df.withColumn('field2', get_json_object(df['parent'], '$[2].field2'))

.select(get_json_object(df['parent'], '$[2].field2').alias('field2'))

显然,可以在此处添加正确类型的转换。

因为我的源 JSON 是一个数组,所以我将每个对象作为数组元素访问。所以 field2 在第三个数组元素中,即 index = 2。这种方法感觉很脆弱,因为数据的顺序现在很重要。但是,也可以指定通配符数组元素以在所有数组元素中进行选择,例如 $[*].field2。此外,子运算符可用于获取嵌套数据,例如 $[2].field3.field3a

目前尚不清楚如何最好地处理重复的字段名称,但以下 JSONPath 将返回一个值数组:

$[*].field1

Return value:
["1", "11"]

请注意,我没有考虑/测试使用 get_json_object 对性能的影响。


推荐阅读