读取Spark 2.2中的数组字段

时间:2018-01-10 20:25:06

标签: json apache-spark apache-spark-dataset

假设您有一堆数据,其行如下所示:

{
    'key': [
        {'key1': 'value11', 'key2': 'value21'},
        {'key1': 'value12', 'key2': 'value22'}
    ]
}

我想把它读成Spark Dataset。一种方法如下:

case class ObjOfLists(k1: List[String], k2: List[String])
case class Data(k: ObjOfLists)

然后你可以这样做:

sparkSession.read.json(pathToData).select(
    struct($"key.key1" as "k1", $"key.key2" as "k2") as "k"
)
.as[Data]

这样可以正常工作,但它有点扼杀数据;毕竟在数据'key'中指向对象列表而不是列表对象。换句话说,我真正想要的是:

case class Obj(k1: String, k2: String)
case class DataOfList(k: List[Obj])

我的问题:我可以在select中添加一些其他语法,以便将生成的Dataframe转换为Dataset[DataOfList]吗?

我尝试使用与上面相同的select语法,并得到:

  

线程“main”中的异常org.apache.spark.sql.AnalysisException:需要一个数组字段但得到struct<k1:array<string>,k2:array<string>>;

所以我也试过了:

sparkSession.read.json(pathToData).select(
    array(struct($"key.key1" as "k1", $"key.key2" as "k2")) as "k"
)
.as[DataOfList]

这会编译并运行,但数据如下所示:

  

DataOfList(列表(的OBJ(org.apache.spark.sql.catalyst.expressions.UnsafeArrayData @ bb2a5516,org.apache.spark.sql.catalyst.expressions.UnsafeArrayData @ bec5e4a7)))

还有其他想法吗?

1 个答案:

答案 0 :(得分:0)

重新制作数据以反映预期的名称:

case class Obj(k1: String, k2: String)
case class DataOfList(k: Seq[Obj])

val text = Seq("""{
  "key": [
    {"key1": "value11", "key2": "value21"},
    {"key1": "value12", "key2": "value22"}
  ]
}""").toDS

val df = spark.read.json(text)

df
  .select($"key".cast("array<struct<k1:string,k2:string>>").as("k"))
  .as[DataOfList]
  .first
DataOfList(List(Obj(value11,value21), Obj(value12,value22)))

使用无关对象,您可以在read:

上定义模式
val textExtended = Seq("""{
  "key": [
    {"key0": "value01", "key1": "value11", "key2": "value21"},
    {"key1": "value12", "key2": "value22", "key3": "value32"}
  ]
}""").toDS

val schemaSubset = StructType(Seq(StructField("key", ArrayType(StructType(Seq(
  StructField("key1", StringType),
  StructField("key2", StringType))))
)))

val df = spark.read.schema(schemaSubset).json(textExtended)

并像以前一样继续。