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?
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")))
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"))
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()
J'utilise cette commande pour résoudre le problème to_json:
output_df = (df.select(to_json(struct(col("*"))).alias("content")))