将单行文件中的多个JSON对象加载到PySpark

时间:2017-12-04 18:05:03

标签: json amazon-s3 pyspark pyspark-sql

我需要一些帮助才能将S3存储桶中的一系列JSON文件存入PySpark DataFrame。

此存储桶中的文件都有.json扩展名,但遗憾的是不遵守通常的Spark要求,即每行有一个JSON对象,而是在方括号内的所有行中。 / p>

所以而不是:

{"first_column": 12, "second_column": {"nested_column": "value"}}
{"first_column": 24, "second_column": {"nested_column": "value2"}}

我有:

[{"first_column": 12, "second_column": {"nested_column": "value"}},{"first_column": 24, "second_column": {"nested_column": "value2"}}]

我们实际上收到了这种格式的文件,并且有很多这样的文件,不幸的是不可能进行任何手动调整。

到目前为止,我尝试过的方法如下:

我尝试使用spark.read.json方法,使用以下语法与通配符*一起加载多个文件。在这种情况下,sparksqlContext

df = spark.read.json("s3://path_to_bucket_*.json") 

运行时不会引发任何错误或警告,并返回所需的架构:

df.printSchema()

root
 |-- first_column: long (nullable = true)
 |-- second_column: struct (nullable = true)
 |     |-- nested_column: string (nullable = true)

但是,当我尝试查看数据时,我得到以下内容:

+------------+-------------+
|first_column|second_column|
+------------+-------------+
|        null|         null|
+------------+-------------+

我找到了一种从Databricks here实际加载数据的方法,它使用Spark上下文sc来读取配对RDD,如下所示:

dfRDD = sc.wholeTextFiles("s3://path_to_bucket_*.json")

这将返回带有文件名和文件正文的PairedRDD。然而,让我感到困惑的是,当我使用来自此RDD的正文信息调用以下行时,它工作正常并且根本没有空值:

df = spark.read.json(dfRDD.map(lambda x: x[1]))

所以,我很困惑为什么会发生这种情况,因为我认为这是相同的信息被输入函数,因为RDD中的文本正文不包含任何换行符,而是包含方括号内的JSON对象(就像我上面显示的第二个例子)。

虽然这是一种解决方法,但遗憾的是缺乏这种方法;首先,使用RDD方法要慢得多,更重要的是,我需要获取我从中获取此信息的文件名的列。我知道当从文件直接加载时,使用input_file_name模块中的pyspark.sql.functions函数可以实现这一点,但是在使用RDD方法时这不起作用。我设法编写了一个纯Python函数,它将每个配对RDD的第一个元素的文件名信息转换为JSON字符串,但这很慢。

如果有人能帮我解决这个问题,我将非常感激。我很欣赏我可能不得不使用RDD方法,但我很困惑为什么spark.read.json在一种情况下完美运行,而不是另一种情况。

1 个答案:

答案 0 :(得分:1)

虽然我不确定是什么导致一个解决方案工作而另一个解决方案没有,但是我能够通过仅使用sql.read.json在某种程度上解决问题。

将read.json中的参数allowComments,allowUnquotedFieldNames,allowSingleQuotes,allowNumericLeadingZero,allowBackslashEscapingAnyCharacter设置为True。通过这种方式,我能够删除空值,并且在数据帧中成功转换了90%的数据而没有任何空值。

查看其他参数here