Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/apache-spark/5.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
来自带有动态模式的_json的Spark_Json_Apache Spark_Apache Spark Sql - Fatal编程技术网

来自带有动态模式的_json的Spark

来自带有动态模式的_json的Spark,json,apache-spark,apache-spark-sql,Json,Apache Spark,Apache Spark Sql,我试图使用Spark处理具有可变结构(嵌套JSON)的JSON数据。输入的JSON数据可能非常大,每行超过1000个键,一批可能超过20GB。 整个批处理由30个数据源生成,每个JSON的“key2”可用于标识源,并预定义了每个源的结构 处理此类数据的最佳方法是什么? 我尝试过使用from_json,如下所示,但它只适用于固定模式,要使用它,首先需要根据每个源对数据进行分组,然后应用模式。 由于数据量很大,我的首选方法是只扫描数据一次,然后根据预定义的模式从每个源中提取所需的值 import o

我试图使用Spark处理具有可变结构(嵌套JSON)的JSON数据。输入的JSON数据可能非常大,每行超过1000个键,一批可能超过20GB。 整个批处理由30个数据源生成,每个JSON的“key2”可用于标识源,并预定义了每个源的结构

处理此类数据的最佳方法是什么? 我尝试过使用from_json,如下所示,但它只适用于固定模式,要使用它,首先需要根据每个源对数据进行分组,然后应用模式。 由于数据量很大,我的首选方法是只扫描数据一次,然后根据预定义的模式从每个源中提取所需的值

import org.apache.spark.sql.types._ 
import spark.implicits._

val data = sc.parallelize(
    """{"key1":"val1","key2":"source1","key3":{"key3_k1":"key3_v1"}}"""
    :: Nil)
val df = data.toDF


val schema = (new StructType)
    .add("key1", StringType)
    .add("key2", StringType)
    .add("key3", (new StructType)
    .add("key3_k1", StringType))


df.select(from_json($"value",schema).as("json_str"))
  .select($"json_str.key3.key3_k1").collect
res17: Array[org.apache.spark.sql.Row] = Array([xxx])

如果你有问题中提到的数据

val data = sc.parallelize(
    """{"key1":"val1","key2":"source1","key3":{"key3_k1":"key3_v1"}}"""
    :: Nil)
您不需要为json数据创建
schema
。Spark sql可以从json字符串推断出
schema
。您只需使用
SQLContext.read.json
,如下所示

val df = sqlContext.read.json(data)
对于上面使用的rdd数据,它将为您提供如下所示的
schema

您只需选择
key3_k1

df2.select("key3.key3_k1").show(false)
//+-------+
//|key3_k1|
//+-------+
//|key3_v1|
//+-------+

您可以随心所欲地操作
数据帧
。我希望答案是有帮助的

我不确定我的建议是否能帮助你,尽管我有一个类似的案例,我解决了它如下:

1) 因此,我们的想法是使用json rapture(或其他json库)来 动态加载JSON模式。例如,你可以阅读第1页 用于发现模式的json文件行(类似于我所做的 这里是jsonSchema)

2) 动态生成模式。首先遍历动态 字段(请注意,我将key3的值投影为Map[String,String]) 并为模式中的每一个添加一个StructField

3) 将生成的模式应用到数据框架中

import rapture.json._
import jsonBackends.jackson._

val jsonSchema = """{"key1":"val1","key2":"source1","key3":{"key3_k1":"key3_v1", "key3_k2":"key3_v2", "key3_k3":"key3_v3"}}"""
val json = Json.parse(jsonSchema)

import scala.collection.mutable.ArrayBuffer
import org.apache.spark.sql.types.StructField
import org.apache.spark.sql.types.{StringType, StructType}

val schema = ArrayBuffer[StructField]()
//we could do this dynamic as well with json rapture
schema.appendAll(List(StructField("key1", StringType), StructField("key2", StringType)))

val items = ArrayBuffer[StructField]()
json.key3.as[Map[String, String]].foreach{
  case(k, v) => {
    items.append(StructField(k, StringType))
  }
}
val complexColumn =  new StructType(items.toArray)
schema.append(StructField("key3", complexColumn))

import org.apache.spark.SparkConf
import org.apache.spark.sql.SparkSession
val sparkConf = new SparkConf().setAppName("dynamic-json-schema").setMaster("local")

val spark = SparkSession.builder().config(sparkConf).getOrCreate()

val jsonDF = spark.read.schema(StructType(schema.toList)).json("""your_path\data.json""")

jsonDF.select("key1", "key2", "key3.key3_k1", "key3.key3_k2", "key3.key3_k3").show()
我使用下一个数据作为输入:

{"key1":"val1","key2":"source1","key3":{"key3_k1":"key3_v11", "key3_k2":"key3_v21", "key3_k3":"key3_v31"}}
{"key1":"val2","key2":"source2","key3":{"key3_k1":"key3_v12", "key3_k2":"key3_v22", "key3_k3":"key3_v32"}}
{"key1":"val3","key2":"source3","key3":{"key3_k1":"key3_v13", "key3_k2":"key3_v23", "key3_k3":"key3_v33"}}
以及输出:

+----+-------+--------+--------+--------+
|key1|   key2| key3_k1| key3_k2| key3_k3|
+----+-------+--------+--------+--------+
|val1|source1|key3_v11|key3_v21|key3_v31|
|val2|source2|key3_v12|key3_v22|key3_v32|
|val2|source3|key3_v13|key3_v23|key3_v33|
+----+-------+--------+--------+--------+
我还没有测试过的一个高级替代方案是,从JSON模式生成一个名为JsonRow的case类,以便拥有一个强类型数据集,该数据集提供了更好的序列化性能,同时使代码更易于维护。要做到这一点,首先需要创建一个JsonRow.scala文件,然后应该实现一个sbt预构建脚本,该脚本将根据源文件动态修改JsonRow.scala的内容(当然,您可能有多个)。要动态生成类JsonRow,可以使用以下代码:

def generateClass(members: Map[String, String], name: String) : Any = {
    val classMembers = for (m <- members) yield {
        s"${m._1}: String"
    }

    val classDef = s"""case class ${name}(${classMembers.mkString(",")});scala.reflect.classTag[${name}].runtimeClass"""
    classDef
  }

祝你好运

这只是对@Ramesh Maharjan答案的重述,但使用了更现代的Spark语法

我在
DataFrameReader
中发现了这个方法,它允许您将
Dataset[String]
中的JSON字符串解析为任意
DataFrame
,并利用Spark在直接从JSON文件读取时使用
Spark.read.JSON(“filepath”)
提供的相同模式推断。每行的模式可以完全不同

def json(jsonDataset: Dataset[String]): DataFrame
用法示例:

val jsonStringDs = spark.createDataset[String](
  Seq(
      ("""{"firstname": "Sherlock", "lastname": "Holmes", "address": {"streetNumber": 121, "street": "Baker", "city": "London"}}"""),
      ("""{"name": "Amazon", "employeeCount": 500000, "marketCap": 817117000000, "revenue": 177900000000, "CEO": "Jeff Bezos"}""")))

jsonStringDs.show

jsonStringDs:org.apache.spark.sql.Dataset[String] = [value: string]
+----------------------------------------------------------------------------------------------------------------------+
|value                                                                                                                 
|
+----------------------------------------------------------------------------------------------------------------------+
|{"firstname": "Sherlock", "lastname": "Holmes", "address": {"streetNumber": 121, "street": "Baker", "city": "London"}}|
|{"name": "Amazon", "employeeCount": 500000, "marketCap": 817117000000, "revenue": 177900000000, "CEO": "Jeff Bezos"}  |
+----------------------------------------------------------------------------------------------------------------------+


val df = spark.read.json(jsonStringDs)
df.show(false)

df:org.apache.spark.sql.DataFrame = [CEO: string, address: struct ... 6 more fields]
+----------+------------------+-------------+---------+--------+------------+------+------------+
|CEO       |address           |employeeCount|firstname|lastname|marketCap   |name  |revenue     |
+----------+------------------+-------------+---------+--------+------------+------+------------+
|null      |[London,Baker,121]|null         |Sherlock |Holmes  |null        |null  |null        |
|Jeff Bezos|null              |500000       |null     |null    |817117000000|Amazon|177900000000|
+----------+------------------+-------------+---------+--------+------------+------+------------+
该方法可从Spark 2.2.0获得:

这是一个想法,而不是一个完整的答案。您的意思是所有模式都共享一些公共密钥,而这些密钥正是您所关心的吗
Json
对象是可序列化的,因此您可以先创建一个
RDD[Json]
,然后选择相关字段。(并非所有Argonaut对象都是可序列化的,因此您必须小心。或者选择不同的JSON库。)有几个常用键(示例中的键1、键2),但我关心变量键。公共键包含有关json的信息,如时间戳、源等,变量部分包含实际信息。您希望的输出是什么?一个数据帧,其列是所有相关键的并集(否则为null)?每个模式一个数据帧?我仍然认为您需要使用一些JSON库。你必须处理30个不同的案例,但我不认为你在任何情况下都可以避免——你有30种不同类型的数据。是的,我正在做30个DFs的“联合所有”。对于每个DF,我在字符串列中提取所需的键VAL。然而,我在一个UDF中尝试了“json镜头”库,它正在工作,但考虑到未来的支持和spark的内置优化,我想尝试spark的json函数。这种方法存在两个问题,1。spark需要很长时间来确定模式,因为我的输入数据很大。2.两个源可以在同一级别(在动态部分中)具有相同的键,但值可以是嵌套的json,在这种情况下,spark将推断模式直到公共key@Syntax,您将必须在您的输入文件中找出更改相同键的方法,抱歉,我帮不了什么忙。谢谢Ramesh的建议。很遗憾,我无法更改源数据。
case class JsonRow(key3_k2: String,key3_k1: String,key1: String,key2: String,key3_k3: String);scala.reflect.classTag[JsonRow].runtimeClass
def json(jsonDataset: Dataset[String]): DataFrame
val jsonStringDs = spark.createDataset[String](
  Seq(
      ("""{"firstname": "Sherlock", "lastname": "Holmes", "address": {"streetNumber": 121, "street": "Baker", "city": "London"}}"""),
      ("""{"name": "Amazon", "employeeCount": 500000, "marketCap": 817117000000, "revenue": 177900000000, "CEO": "Jeff Bezos"}""")))

jsonStringDs.show

jsonStringDs:org.apache.spark.sql.Dataset[String] = [value: string]
+----------------------------------------------------------------------------------------------------------------------+
|value                                                                                                                 
|
+----------------------------------------------------------------------------------------------------------------------+
|{"firstname": "Sherlock", "lastname": "Holmes", "address": {"streetNumber": 121, "street": "Baker", "city": "London"}}|
|{"name": "Amazon", "employeeCount": 500000, "marketCap": 817117000000, "revenue": 177900000000, "CEO": "Jeff Bezos"}  |
+----------------------------------------------------------------------------------------------------------------------+


val df = spark.read.json(jsonStringDs)
df.show(false)

df:org.apache.spark.sql.DataFrame = [CEO: string, address: struct ... 6 more fields]
+----------+------------------+-------------+---------+--------+------------+------+------------+
|CEO       |address           |employeeCount|firstname|lastname|marketCap   |name  |revenue     |
+----------+------------------+-------------+---------+--------+------------+------+------------+
|null      |[London,Baker,121]|null         |Sherlock |Holmes  |null        |null  |null        |
|Jeff Bezos|null              |500000       |null     |null    |817117000000|Amazon|177900000000|
+----------+------------------+-------------+---------+--------+------------+------+------------+