【问题标题】:Extract array from list of json strings using Spark使用 Spark 从 json 字符串列表中提取数组
【发布时间】:2022-09-28 01:43:58
【问题描述】:

我的数据框中有一列包含 JSON 列表,但类型是字符串。我需要在这个专栏上运行explode,所以首先我需要将它转换成一个列表。我找不到对这个用例的太多引用。

样本数据:

columnName: \"[{\"name\":\"a\",\"info\":{\"age\":\"1\",\"grade\":\"b\"},\"other\":7},{\"random\":\"x\"}, {...}]\"

以上是数据的样子,字段不是固定的(索引 0 可能包含带有某些字段的 JSON,而索引 1 将包含带有其他字段的字段)。在列表中可以有更多嵌套的 JSON 或一些额外的字段。我目前正在使用这个 -

\"\"\"explode(split(regexp_replace(regexp_replace(colName, \'(\\\\\\},)\',\'}},\'), \'(\\\\\\[|\\\\\\])\',\'\'), \"},\")) as colName\"\"\" 我只是将 \"},\" 替换为 \"}},\" 然后删除 \"[]\" 然后在 \"},\" 上调用 split 但这种方法不起作用,因为有嵌套的 JSON。

如何从字符串中提取数组?

  • 使用正确的输入 JSON 更新问题
  • 没错,大约有 20 到 30 个字段都可以为空,我尝试通过示例来展示这一点。有什么具体要检查的吗?

标签: json scala apache-spark apache-spark-sql


【解决方案1】:

你可以这样试试:

// Initial DataFrame

df.show(false)

+----------------------------------------------------------------------+
|columnName                                                            |
+----------------------------------------------------------------------+
|[{"name":"a","info":{"age":"1","grade":"b"},"other":7},{"random":"x"}]|
+----------------------------------------------------------------------+

df.printSchema()

root
 |-- columnName: string (nullable = true)
 
// toArray is a user defined function that parses an array of json objects which is present as a string
     
import org.json.JSONArray

val toArray = udf { (data: String) => {
    val jsonArray = new JSONArray(data)
    var arr: Array[String] = Array()
    val objects = (0 until jsonArray.length).map(x => jsonArray.getJSONObject(x))
    objects.foreach { elem =>
        arr :+= elem.toString
    }
    arr
}
}

// Using the udf and exploding the resultant array

val df1 = df.withColumn("columnName",explode(toArray(col("columnName"))))

df1.show(false)

+-----------------------------------------------------+
|columnName                                           |
+-----------------------------------------------------+
|{"other":7,"name":"a","info":{"grade":"b","age":"1"}}|
|{"random":"x"}                                       |
+-----------------------------------------------------+

df1.printSchema()

root
 |-- columnName: string (nullable = true)
 
// Parsing the json string by obtaining the schema dynamically

val schema = spark.read.json(df1.select("columnName").rdd.map(x => x(0).toString)).schema
val df2 = df1.withColumn("columnName",from_json(col("columnName"),schema))

df2.show(false)

+---------------+
|columnName     |
+---------------+
|[[1, b], a, 7,]|
|[,,, x]        |
+---------------+

df2.printSchema()

root
 |-- columnName: struct (nullable = true)
 |    |-- info: struct (nullable = true)
 |    |    |-- age: string (nullable = true)
 |    |    |-- grade: string (nullable = true)
 |    |-- name: string (nullable = true)
 |    |-- other: long (nullable = true)
 |    |-- random: string (nullable = true)
 
// Extracting all the fields from the json

df2.select(col("columnName.*")).show(false)

+------+----+-----+------+
|info  |name|other|random|
+------+----+-----+------+
|[1, b]|a   |7    |null  |
|null  |null|null |x     |
+------+----+-----+------+

编辑:

如果可以使用get_json_object函数,可以尝试这种方式

// Get the list of columns dynamically

val columns = spark.read.json(df1.select("columnName").rdd.map(x => x(0).toString)).columns

// define an empty array of Column type and get_json_object function to extract the columns

var extract_columns: Array[Column] = Array()
    columns.foreach { column =>
    extract_columns :+= get_json_object(col("columnName"), "$." + column).as(column)
}

df1.select(extract_columns: _*).show(false)

+-----------------------+----+-----+------+
|info                   |name|other|random|
+-----------------------+----+-----+------+
|{"grade":"b","age":"1"}|a   |7    |null  |
|null                   |null|null |x     |
+-----------------------+----+-----+------+

请注意,info 列不是结构类型。您可能必须遵循类似的方式来提取嵌套 json 的列

【讨论】:

  • 我喜欢第二种方法,但我使用的是旧版本的 Spark,所以不能“from_json”,是否可以使用“get_json_object”来实现?第一种方法似乎也不错,但正在寻找我可以重用的 Spark 库。
  • 第二种方法似乎与我正在寻找的不同。第一个工作就像一个魅力。谢谢。
【解决方案2】:

val testString = """[{"name":"a","info":{"age":"1","grade":"b"},"other":7},{"random": “X”}]”””

spark.read.json(Seq(testString).toDS()) .select("info.age", "info.grade","name","other","random") .show(10,假)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-07-27
    • 2020-11-07
    • 1970-01-01
    • 1970-01-01
    • 2014-07-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多