Spark Streaming Scala将不同结构的json组合成一个数据帧
我正在尝试处理来自Kinesis的Json字符串。Json字符串可以有两种不同的形式。从Kinesis,我创建了一个数据流:Spark Streaming Scala将不同结构的json组合成一个数据帧,json,scala,apache-spark,Json,Scala,Apache Spark,我正在尝试处理来自Kinesis的Json字符串。Json字符串可以有两种不同的形式。从Kinesis,我创建了一个数据流: val kinesisStream = KinesisUtils.createStream( ssc, appName, "Kinesis_Stream", "kinesis.ap-southeast-1.amazonaws.com", "region", InitialPositionInStream.LATEST, kinesisCheckpointInterva
val kinesisStream = KinesisUtils.createStream(
ssc, appName, "Kinesis_Stream", "kinesis.ap-southeast-1.amazonaws.com",
"region", InitialPositionInStream.LATEST, kinesisCheckpointInterval, StorageLevel.MEMORY_AND_DISK_2)
val lines = kinesisStream.map(x => new String(x))
lines.foreachRDD((rdd, time) =>{
val sqlContext = SQLContextSingleton.getInstance(rdd.sparkContext)
import sqlContext.implicits.StringToColumn
if(rdd.count() > 0){
// Process jsons here
// Json strings here would have either one of the formats below
}
})
RDD字符串将具有这些json字符串中的任意一个。
收藏:
[
{
"data": {
"ApplicationVersion": "1.0.3 (65)",
"ProjectId": 30024,
"TargetId": "4138",
"Timestamp": 0
},
"host": "host1"
},
{
"data": {
"ApplicationVersion": "1.0.3 (65)",
"ProjectId": 30025,
"TargetId": "4139",
"Timestamp": 0
},
"host": "host1"
}
]
一些Json字符串是单一对象,如下所示:
{
"ApplicationVersion": "1.0.3 (65)",
"ProjectId": 30026,
"TargetId": "4140",
"Timestamp": 0
}
我希望能够从“data”键提取对象,如果它是第一种类型的Json字符串,并与第二种类型的Json相结合,形成一个RDD/DataFrame,我如何实现这一点
最终,我希望我的数据框架是这样的:
+------------------+---------+--------+---------+
|ApplicationVersion|ProjectId|TargetId|Timestamp|
+------------------+---------+--------+---------+
| 1.0.3 (65)| 30024| 4138| 0|
| 1.0.3 (65)| 30025| 4139| 0|
| 1.0.3 (65)| 30026| 4140| 0|
+------------------+---------+--------+---------+
对不起,Scala和Spark是新手。我一直在查看现有的示例,但不幸的是,还没有找到解决方案
非常感谢。您可以在选择第一个
数据框中的列后使用union。*
列
:
val spark=SparkSession.builder().master(“local[*]).getOrCreate()
val sc=spark.sparkContext
//假设您将JSON存储在两个单独的字符串'json1'和'json2'中`
val df1=spark.read.json(sc.parallelize(Seq(json1)))
val df2=spark.read.json(sc.parallelize(Seq(json2)))
导入spark.implicits_
df1.select($“data.*”)//仅选择第一个数据帧中的数据列
.union(df2)//合并两个数据帧,因为它们具有相同的结构
.show()
编辑[其他解决方案链接]
编辑完问题后,我了解到在解析JSON文件时需要某种回退机制。使用任何JSON解析库都有很多方法可以做到这一点——Play提供了一个很好的解决方案,我认为它已经解释了如何以优雅的方式解决这个问题
一旦你有了一个RDD[Data]
其中数据是你的“变量”类型,你可以简单地使用RDD.toDF()
将它转换成一个Dataframe
希望对您有所帮助。此示例使用:
谢谢你的快速回复!抱歉,我忘了提到我使用的是Spark Streaming数据流,我已经更新了我的问题。尽管如此,您的回答仍然很有帮助!如果您能够从数据流中提取字符串,代码应该或多或少可以工作。谢谢!通过使用json4s,这为我指明了正确的方向。这使我能够在转换为DFthanks之前首先处理json字符串,以便快速响应Andrei,我很感激,这非常有用!抱歉,我忘了提到我使用的是Spark Streaming数据流,我已经更新了上面的问题。我明白了。这是一个简单的方法来知道哪一个对象时,不幸的是我已经编辑了答案。这会帮助你实现你想要的,我只是想避免把所有的细节放在这里,因为这实际上是两个独立的问题。我希望它能帮助你:)
import org.json4s._
import org.json4s.jackson.JsonMethods._
implicit val format = DefaultFormats
case class jsonschema ( ApplicationVersion: String, ProjectId: String, TargetId: String, Timestamp:Int )
val string1 = """
[ {
"data" : {
"ApplicationVersion" : "1.0.3 (65)",
"ProjectId" : 30024,
"TargetId" : "4138",
"Timestamp" : 0
},
"host" : "host1"
}, {
"data" : {
"ApplicationVersion" : "1.0.3 (65)",
"ProjectId" : 30025,
"TargetId" : "4139",
"Timestamp" : 0
},
"host" : "host1"
} ]
"""
val string2 = """
[ {
"ApplicationVersion" : "1.0.3 (65)",
"ProjectId" : 30025,
"TargetId" : "4140",
"Timestamp" : 0
}, {
"ApplicationVersion" : "1.0.3 (65)",
"ProjectId" : 30025,
"TargetId" : "4141",
"Timestamp" : 0
} ]
"""
val json1 = (parse(string1) \ "data").extract[List[jsonschema]]
val json2 = parse(string2).extract[List[jsonschema]]
val jsonRDD = json1.union(json2)
val df = sqlContext.createDataFrame(jsonRDD)
df.show
+------------------+---------+--------+---------+
|ApplicationVersion|ProjectId|TargetId|Timestamp|
+------------------+---------+--------+---------+
| 1.0.3 (65)| 30024| 4138| 0|
| 1.0.3 (65)| 30025| 4139| 0|
| 1.0.3 (65)| 30025| 4140| 0|
| 1.0.3 (65)| 30025| 4141| 0|
+------------------+---------+--------+---------+