使用 Spray JSON 将 Spark DataFrame 解析为 JSON Object/Array 的通用方法
Generic way to Parse Spark DataFrame to JSON Object/Array Using Spray JSON
我正在尝试找到一种通用方法(不使用 Scala 中的具体案例 class) 将 Spark DataFrame 解析为 JSON Object/Array 使用 Spray JSON 或任何其他库。
我尝试使用 spray-json 来解决这个问题,我当前的代码看起来像这样
import spray.json._
import spray.json.DefaultJsonProtocol._
val list = sc.parallelize(List(("a1","b1","c1","d1"),("a2","b2","c2","d2"))).toDF
list.show
+---+---+---+---+
| _1| _2| _3| _4|
+---+---+---+---+
| a1| b1| c1| d1|
| a2| b2| c2| d2|
+---+---+---+---+
val json = list.toJSON.collect.toJson.prettyPrint
println(json)
当前输出:
["{\"_1\":\"a1\",\"_2\":\"b1\",\"_3\":\"c1\",\"_4\":\"d1\"}", "{\"_1\":\"a2\",\"_2\":\"b2\",\"_3\":\"c2\",\"_4\":\"d2\"}"]
预期输出:
[{
"_1": "a1",
"_2": "b1",
"_3": "c1",
"_4": "d1"
}, {
"_1": "a2",
"_2": "b2",
"_3": "c2",
"_4": "d2"
}]
请建议如何在不使用 "concrete scala case class" 的情况下获得所需格式的预期输出。使用 spray-json 或任何其他库。
我得到了 earlier post 的帮助。如果你看过这里,我想你会得到答案。
你说对了一半。通过添加自定义格式化代码,您应该能够获得所需格式的输出。
import scala.util.parsing.json.JSON
import scala.util.parsing.json.JSONArray
import scala.util.parsing.json.JSONFormat
import scala.util.parsing.json.JSONObject
import scala.util.parsing.json.JSONType
// Thanks to Senia for providing this in her solution
def format(t: Any, i: Int = 0): String = t match {
case o: JSONObject =>
o.obj.map{ case (k, v) =>
" "*(i+1) + JSONFormat.defaultFormatter(k) + ": " + format(v, i+1)
}.mkString("{\n", ",\n", "\n" + " "*i + "}")
case a: JSONArray =>
a.list.map{
e => " "*(i+1) + format(e, i+1)
}.mkString("[\n", ",\n", "\n" + " "*i + "]")
case _ => JSONFormat defaultFormatter t
}
val list = sc.parallelize(List(("a1","b1","c1","d1"),("a2","b2","c2","d2"))).toDF
// Create array
val jsonArray = list.toJSON.collect()
val jsonFormattedArray = jsonArray.map(j => format(JSON.parseRaw(j).get))
res1: Array[String] =
Array({
"_1": "a1",
"_2": "b1",
"_3": "c1",
"_4": "d1"
}, {
"_1": "a2",
"_2": "b2",
"_3": "c2",
"_4": "d2"
})
将格式化的Json转换为字符串
scala> jsonFormattedArray.toList.mkString(",")
res2: String =
{
"_1": "a1",
"_2": "b1",
"_3": "c1",
"_4": "d1"
},{
"_1": "a2",
"_2": "b2",
"_3": "c2",
"_4": "d2"
}
在尝试使用各种库的各种方法后,我最终选择了以下简单方法。
val list = sc.parallelize(List(("a1","b1","c1","d1"),("a2","b2","c2","d2"))).toDF
val jsonArray = list.toJSON.collect
/*jsonArray: Array[String] = Array({"_1":"a1","_2":"b1","_3":"c1","_4":"d1"}, {"_1":"a2","_2":"b2","_3":"c2","_4":"d2"})*/
val finalOutput = jsonArray.mkString("[", ",", "]")
/*finalOutput: String = [{"_1":"a2","_2":"b2","_3":"c2","_4":"d2"},{"_1":"a1","_2":"b1","_3":"c1","_4":"d1"}]*/
在这种方法中,我们不需要使用 spray-JSON 或任何其他库。
特别感谢@Aman Sehgal。他的回答帮助我想出了这个最佳解决方案。
注意:我尚未使用大型 DF 分析此方法的性能,但通过一些基本的性能测试,它看起来与 "spray-json" 的“.toJson.prettyPrint”一样快。
我正在尝试找到一种通用方法(不使用 Scala 中的具体案例 class) 将 Spark DataFrame 解析为 JSON Object/Array 使用 Spray JSON 或任何其他库。
我尝试使用 spray-json 来解决这个问题,我当前的代码看起来像这样
import spray.json._
import spray.json.DefaultJsonProtocol._
val list = sc.parallelize(List(("a1","b1","c1","d1"),("a2","b2","c2","d2"))).toDF
list.show
+---+---+---+---+
| _1| _2| _3| _4|
+---+---+---+---+
| a1| b1| c1| d1|
| a2| b2| c2| d2|
+---+---+---+---+
val json = list.toJSON.collect.toJson.prettyPrint
println(json)
当前输出:
["{\"_1\":\"a1\",\"_2\":\"b1\",\"_3\":\"c1\",\"_4\":\"d1\"}", "{\"_1\":\"a2\",\"_2\":\"b2\",\"_3\":\"c2\",\"_4\":\"d2\"}"]
预期输出:
[{
"_1": "a1",
"_2": "b1",
"_3": "c1",
"_4": "d1"
}, {
"_1": "a2",
"_2": "b2",
"_3": "c2",
"_4": "d2"
}]
请建议如何在不使用 "concrete scala case class" 的情况下获得所需格式的预期输出。使用 spray-json 或任何其他库。
我得到了 earlier post 的帮助。如果你看过这里,我想你会得到答案。
你说对了一半。通过添加自定义格式化代码,您应该能够获得所需格式的输出。
import scala.util.parsing.json.JSON
import scala.util.parsing.json.JSONArray
import scala.util.parsing.json.JSONFormat
import scala.util.parsing.json.JSONObject
import scala.util.parsing.json.JSONType
// Thanks to Senia for providing this in her solution
def format(t: Any, i: Int = 0): String = t match {
case o: JSONObject =>
o.obj.map{ case (k, v) =>
" "*(i+1) + JSONFormat.defaultFormatter(k) + ": " + format(v, i+1)
}.mkString("{\n", ",\n", "\n" + " "*i + "}")
case a: JSONArray =>
a.list.map{
e => " "*(i+1) + format(e, i+1)
}.mkString("[\n", ",\n", "\n" + " "*i + "]")
case _ => JSONFormat defaultFormatter t
}
val list = sc.parallelize(List(("a1","b1","c1","d1"),("a2","b2","c2","d2"))).toDF
// Create array
val jsonArray = list.toJSON.collect()
val jsonFormattedArray = jsonArray.map(j => format(JSON.parseRaw(j).get))
res1: Array[String] =
Array({
"_1": "a1",
"_2": "b1",
"_3": "c1",
"_4": "d1"
}, {
"_1": "a2",
"_2": "b2",
"_3": "c2",
"_4": "d2"
})
将格式化的Json转换为字符串
scala> jsonFormattedArray.toList.mkString(",")
res2: String =
{
"_1": "a1",
"_2": "b1",
"_3": "c1",
"_4": "d1"
},{
"_1": "a2",
"_2": "b2",
"_3": "c2",
"_4": "d2"
}
在尝试使用各种库的各种方法后,我最终选择了以下简单方法。
val list = sc.parallelize(List(("a1","b1","c1","d1"),("a2","b2","c2","d2"))).toDF
val jsonArray = list.toJSON.collect
/*jsonArray: Array[String] = Array({"_1":"a1","_2":"b1","_3":"c1","_4":"d1"}, {"_1":"a2","_2":"b2","_3":"c2","_4":"d2"})*/
val finalOutput = jsonArray.mkString("[", ",", "]")
/*finalOutput: String = [{"_1":"a2","_2":"b2","_3":"c2","_4":"d2"},{"_1":"a1","_2":"b1","_3":"c1","_4":"d1"}]*/
在这种方法中,我们不需要使用 spray-JSON 或任何其他库。
特别感谢@Aman Sehgal。他的回答帮助我想出了这个最佳解决方案。
注意:我尚未使用大型 DF 分析此方法的性能,但通过一些基本的性能测试,它看起来与 "spray-json" 的“.toJson.prettyPrint”一样快。