web-dev-qa-db-fra.com

Spark Row to JSON

Je voudrais créer un JSON à partir d'une trame de données Spark v.1.6 (en utilisant scala). Je sais qu'il existe la solution simple de faire df.toJSON.

Cependant, mon problème semble un peu différent. Considérons par exemple une trame de données avec les colonnes suivantes:

|  A  |     B     |  C1  |  C2  |    C3   |
-------------------------------------------
|  1  | test      |  ab  |  22  |  TRUE   |
|  2  | mytest    |  gh  |  17  |  FALSE  |

J'aimerais avoir à la fin une trame de données avec

|  A  |     B     |                        C                   |
----------------------------------------------------------------
|  1  | test      | { "c1" : "ab", "c2" : 22, "c3" : TRUE }    |
|  2  | mytest    | { "c1" : "gh", "c2" : 17, "c3" : FALSE }   |

où C est un JSON contenant C1, C2, C3. Malheureusement, au moment de la compilation, je ne sais pas à quoi ressemble la trame de données (sauf les colonnes A et B qui sont toujours "fixes").

Quant à la raison pour laquelle j'en ai besoin: j'utilise Protobuf pour envoyer les résultats. Malheureusement, mon dataframe a parfois plus de colonnes que prévu et je les enverrais toujours via Protobuf, mais je ne veux pas spécifier toutes les colonnes dans la définition.

Comment puis-je atteindre cet objectif?

14
navige

Spark 2.1 devrait avoir un support natif pour ce cas d'utilisation (voir # 15354 ).

import org.Apache.spark.sql.functions.to_json
df.select(to_json(struct($"c1", $"c2", $"c3")))
19
Michael Armbrust

Commençons par convertir les C en struct:

val dfStruct = df.select($"A", $"B", struct($"C1", $"C2", $"C3").alias("C"))

Cette structure peut être convertie en JSONL en utilisant toJSON comme précédemment:

dfStruct.toJSON.collect
// Array[String] = Array(
//   {"A":1,"B":"test","C":{"C1":"ab","C2":22,"C3":true}}, 
//   {"A":2,"B":"mytest","C":{"C1":"gh","C2":17,"C3":false}})

Je ne connais aucune méthode intégrée qui puisse convertir une seule colonne, mais vous pouvez la convertir individuellement et join ou utiliser votre analyseur JSON préféré dans un UDF.

case class C(C1: String, C2: Int, C3: Boolean)

object CJsonizer {
  import org.json4s._
  import org.json4s.JsonDSL._
  import org.json4s.jackson.Serialization
  import org.json4s.jackson.Serialization.write

  implicit val formats = Serialization.formats(org.json4s.NoTypeHints)

  def toJSON(c1: String, c2: Int, c3: Boolean) = write(C(c1, c2, c3))
}


val cToJSON = udf((c1: String, c2: Int, c3: Boolean) => 
  CJsonizer.toJSON(c1, c2, c3))

df.withColumn("c_json", cToJSON($"C1", $"C2", $"C3"))
5
zero323

Ici, pas d'analyseur JSON, et il s'adapte à votre schéma:

import org.Apache.spark.sql.functions.{col, concat, concat_ws, lit}

df.select(
  col(df.columns(0)),
  col(df.columns(1)),
  concat(
    lit("{"), 
    concat_ws(",",df.dtypes.slice(2, df.dtypes.length).map(dt => {
      val c = dt._1;
      val t = dt._2;
      concat(
        lit("\"" + c + "\":" + (if (t == "StringType") "\""; else "")  ),
        col(c),
        lit(if(t=="StringType") "\""; else "") 
      )
    }):_*), 
    lit("}")
  ) as "C"
).collect()
5
David Griffin

J'utilise cette commande pour résoudre le problème to_json:

output_df = (df.select(to_json(struct(col("*"))).alias("content")))
2
Cyanny