在spark中编写JSON时保留具有空值的键

时间:2017-05-30 20:44:50

标签: java json apache-spark apache-spark-sql

我正在尝试使用spark编写JSON文件。有些键的值为null。这些在DataSet中显示得很好,但是当我写文件时,键被删除了。我如何确保他们被保留?

编写文件的代码:

ddp.coalesce(20).write().mode("overwrite").json("hdfs://localhost:9000/user/dedupe_employee");

来自源代码的JSON数据的一部分:

"event_header": {
        "accept_language": null,
        "app_id": "App_ID",
        "app_name": null,
        "client_ip_address": "IP",
        "event_id": "ID",
        "event_timestamp": null,
        "offering_id": "Offering",
        "server_ip_address": "IP",
        "server_timestamp": 1492565987565,
        "topic_name": "Topic",
        "version": "1.0"
    }

输出:

"event_header": {
        "app_id": "App_ID",
        "client_ip_address": "IP",
        "event_id": "ID",
        "offering_id": "Offering",
        "server_ip_address": "IP",
        "server_timestamp": 1492565987565,
        "topic_name": "Topic",
        "version": "1.0"
    }

在上面的示例中,键accept_languageapp_nameevent_timestamp已被删除。

3 个答案:

答案 0 :(得分:3)

显然,spark不提供处理空值的任何选项。因此,遵循自定义解决方案应该有效。

import com.fasterxml.jackson.module.scala.DefaultScalaModule
import com.fasterxml.jackson.module.scala.experimental.ScalaObjectMapper
import com.fasterxml.jackson.databind.ObjectMapper

case class EventHeader(accept_language:String,app_id:String,app_name:String,client_ip_address:String,event_id: String,event_timestamp:String,offering_id:String,server_ip_address:String,server_timestamp:Long,topic_name:String,version:String)

val ds = Seq(EventHeader(null,"App_ID",null,"IP","ID",null,"Offering","IP",1492565987565L,"Topic","1.0")).toDS()

val ds1 = ds.mapPartitions(records => {
val mapper = new ObjectMapper with ScalaObjectMapper
mapper.registerModule(DefaultScalaModule)
records.map(mapper.writeValueAsString(_))
})

ds1.coalesce(1).write.text("hdfs://localhost:9000/user/dedupe_employee")

这将产生输出:

{"accept_language":null,"app_id":"App_ID","app_name":null,"client_ip_address":"IP","event_id":"ID","event_timestamp":null,"offering_id":"Offering","server_ip_address":"IP","server_timestamp":1492565987565,"topic_name":"Topic","version":"1.0"}

答案 1 :(得分:1)

如果您使用的是Spark 3,则可以添加

spark.sql.jsonGenerator.ignoreNullFields false

答案 2 :(得分:0)

ignoreNullFields 是一个选项,用于设置您希望从 Spark 3 开始将 DataFrame 转换为 json 文件。

如果您需要 Spark 2(特别是 PySpark 2.4.6),您可以尝试使用 Python dict 格式将 DataFrame 转换为 rdd。然后调用 pyspark.rdd.saveTextFile 将 json 文件输出到 hdfs。以下示例可能会有所帮助。

cols = ddp.columns
ddp_ = ddp.rdd
ddp_ = ddp_.map(lambda row: dict([(c, row[c]) for c in cols])
ddp_ = ddp.repartition(1).saveAsTextFile(your_hdfs_file_path)

这应该产生输出文件,如,

{"accept_language": None, "app_id":"123", ...}
{"accept_language": None, "app_id":"456", ...}

此外,如果您想用 JSON None 替换 Python null,您需要将每个 dict 转储到 json 中。

ddp_ = ddp_.map(lambda row: json.dumps(row, ensure.ascii=False))