簡體   English   中英

將 Spark DataFrame 中的 JSON 解析為新列

[英]Parsing JSON within a Spark DataFrame into new columns

背景

我有一個看起來像這樣的 dataframe:

------------------------------------------------------------------------
|name   |meals                                                         |
------------------------------------------------------------------------
|Tom    |{"breakfast": "banana", "lunch": "sandwich"}                  |
|Alex   |{"breakfast": "yogurt", "lunch": "pizza", "dinner": "pasta"}  |
|Lisa   |{"lunch": "sushi", "dinner": "lasagna", "snack": "apple"}     |
------------------------------------------------------------------------

從以下獲得:

var rawDf = Seq(("Tom",s"""{"breakfast": "banana", "lunch": "sandwich"}""" ),
  ("Alex", s"""{"breakfast": "yogurt", "lunch": "pizza", "dinner": "pasta"}"""),
  ("Lisa", s"""{"lunch": "sushi", "dinner": "lasagna", "snack": "apple"}""")).toDF("name", "meals")

我想將其轉換為如下所示的 dataframe:

------------------------------------------------------------------------
|name   |meal       |food                                              |
------------------------------------------------------------------------
|Tom    |breakfast  | banana                                           |
|Tom    |lunch      | sandwich                                         |
|Alex   |breakfast  | yogurt                                           |
|Alex   |lunch      | pizza                                            |
|Alex   |dinner     | pasta                                            |
|Lisa   |lunch      | sushi                                            |
|Lisa   |dinner     | lasagna                                          |
|Lisa   |snack      | apple                                            |
------------------------------------------------------------------------

我使用的是 Spark 2.1,所以我使用 get_json_object 解析 json。 目前,我正在嘗試使用如下所示的中間 dataframe 獲得最終的 dataframe:

------------------------------------------------------------------------
|name   |breakfast |lunch    |dinner  |snack                           |
------------------------------------------------------------------------
|Tom    |banana    |sandwich |null    |null                            |
|Alex   |yogurt    |pizza    |pasta   |null                            |
|Lisa   |null      |sushi    |lasagna |apple                           |
------------------------------------------------------------------------

從以下獲得:

val intermediaryDF = rawDf.select(col("name"),
  get_json_object(col("meals"), "$." + Meals.breakfast).alias(Meals.breakfast),
  get_json_object(col("meals"), "$." + Meals.lunch).alias(Meals.lunch),
  get_json_object(col("meals"), "$." + Meals.dinner).alias(Meals.dinner),
  get_json_object(col("meals"), "$." + Meals.snack).alias(Meals.snack))

Meals是在另一個文件中定義的,該文件的條目比breakfastlunchdinnersnack多得多,但它看起來像這樣:

object Meals {
  val breakfast = "breakfast"
  val lunch = "lunch"
  val dinner = "dinner"
  val snack = "snack"
}

然后我使用intermediaryDF計算最終的 DataFrame,如下所示:

val finalDF = parsedDF.where(col("breakfast").isNotNull).select(col("name"), col("breakfast")).union(
parsedDF.where(col("lunch").isNotNull).select(col("name"), col("lunch"))).union(
parsedDF.where(col("dinner").isNotNull).select(col("name"), col("dinner"))).union(
parsedDF.where(col("snack").isNotNull).select(col("name"), col("snack")))

我的問題

如果我只有幾種Meals類型,則使用中介 DataFrame 可以工作,但實際上我有 40 種,並且枚舉它們中的每一個來計算intermediaryDF是不切實際的。 我也不喜歡一開始就必須計算這個 DF 的想法。 有沒有辦法直接從我的原始 dataframe 到最終的 dataframe 沒有中間步驟,也沒有明確說明Meals中的每個值的情況?

Apache Spark 支持解析 json 數據,但應該有一個預定義的模式才能正確解析它。 您的 json 數據是動態的,因此您不能依賴模式。

一種方法不要讓 apache spark 解析數據,但您可以以鍵值方式解析它(例如,通過使用類似Map[String, String]的東西,這是非常通用的)

您可以這樣做:

將 Jackson json 映射器用於 scala

// mapper object created on each executor node
  val mapper = new ObjectMapper with ScalaObjectMapper
  mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
  mapper.registerModule(DefaultScalaModule)

  val valueAsMap = mapper.readValue[Map[String, String]](s"""{"breakfast": "banana", "lunch": "sandwich"}""")

這將為您提供類似於將 json 字符串轉換為 Map[String, String] 的內容。 這也可以看作是(鍵,值)對的列表

List((breakfast,banana), (lunch,sandwich))

現在,Apache Spark 部件出現了。 定義一個自定義的用戶定義的 function 來解析字符串和 output 的(鍵,值)對列表

val jsonToArray = udf((json:String) => {
    mapper.readValue[Map[String, String]](json).toList
  })

將該轉換應用於“餐”列,並將其轉換為數組類型的列。 之后在那列上explode ,select 作為列food的鍵條目和作為列meal的值條目

val df1 = rowDf.select(col("name"), explode(jsonToArray(col("meals"))).as("meals"))

df1.select(col("name"), col("meals._1").as("meal"), col("meals._2").as("food"))

顯示它輸出的最后一個 dataframe:

|name|     meal|    food|
+----+---------+--------+
| Tom|breakfast|  banana|
| Tom|    lunch|sandwich|
|Alex|breakfast|  yogurt|
|Alex|    lunch|   pizza|
|Alex|   dinner|   pasta|
|Lisa|    lunch|   sushi|
|Lisa|   dinner| lasagna|
|Lisa|    snack|   apple|
+----+---------+--------+

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM