数据帧Spark scala爆炸json数组

时间:2017-03-16 19:18:20

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

我们假设我有一个如下所示的数据框:

+--------------------+--------------------+--------------------------------------------------------------+
|                id  |           Name     |                                                       Payment|
+--------------------+--------------------+--------------------------------------------------------------+
|                1   |           James    |[ {"@id": 1, "currency":"GBP"},{"@id": 2, "currency": "USD"} ]|
+--------------------+--------------------+--------------------------------------------------------------+

架构是:

|-- id: integer (nullable = true)
|-- Name: string (nullable = true)   
|-- Payment: string (nullable = true)

如何将上述JSON数组分解为:

+--------------------+--------------------+-------------------------------+
|                id  |           Name     |                        Payment|
+--------------------+--------------------+-------------------------------+
|                1   |           James    |   {"@id":1, "currency":"GBP"} |
+--------------------+--------------------+-------------------------------+
|                1   |           James    |   {"@id":2, "currency":"USD"} |
+--------------------+--------------------+-------------------------------+

我一直在尝试使用如下所示的爆炸功能,但它无法正常工作。它给出了一个关于无法分解字符串类型的错误,并且它需要一个映射或数组。这是有道理的,因为架构表示它是一个字符串,而不是数组/地图,但我不知道如何将其转换为适当的格式。

val newDF = dataframe.withColumn("nestedPayment", explode(dataframe.col("Payment")))

非常感谢任何帮助!

3 个答案:

答案 0 :(得分:6)

您必须将JSON字符串解析为JSON的数组,然后对结果使用explode(explode需要数组)。

要做到这一点(假设Spark 2.0。* ):

  • 如果您知道所有Payment值都包含表示具有相同大小的数组的json(在本例中为2),则可以硬编码提取第一个和第二个元素,将它们包装在一个数组并爆炸:

    val newDF = dataframe.withColumn("Payment", explode(array(
      get_json_object($"Payment", "$[0]"),
      get_json_object($"Payment", "$[1]")
    )))
    
  • 如果您不能保证所有记录都有一个带有2个元素数组的JSON,但是可以保证这些数组的最大长度,那么可以使用此技巧解析最大大小的元素,然后过滤掉生成的null

    val maxJsonParts = 3 // whatever that number is...
    val jsonElements = (0 until maxJsonParts)
                         .map(i => get_json_object($"Payment", s"$$[$i]"))
    
    val newDF = dataframe
      .withColumn("Payment", explode(array(jsonElements: _*)))
      .where(!isnull($"Payment")) 
    

答案 1 :(得分:0)

您可以使用 ArrayType 定义 Payment json 数组的架构。

php artisan config:clear

然后将 from_json 与此架构一起使用后爆炸将返回所需的结果。

import org.apache.spark.sql.types._

val paymentSchema = ArrayType(StructType(
                  Array(
                        StructField("@id", DataTypes.IntegerType),
                        StructField("currency", DataTypes.StringType)
                  )
))

答案 2 :(得分:-1)

import org.apache.spark.sql.types._

val newDF = dataframe.withColumn("Payment", 
explode(
from_json(
  get_json_object($"Payment", "$."),ArrayType(StringType)
)))