将包含Json的数据集<string>转换为数据集<structtype>

时间:2019-05-23 13:03:40

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

Spark非常擅长将JSON解析为磁盘上的初始读取中的嵌套StructType,但是如果我已经在String中有一个包含JSON的Dataset列了,我想要要将其映射到带有Dataset列的StructType列中,并使用模式推断将整个数据集都考虑在内,同时充分利用并行状态并避免减少操作?

我知道函数schema_of_jsonfrom_json,显然打算一起使用以完成此任务或类似的操作,但是我很难找到实际的工作代码示例,尤其是在Java中

我将接受任何提供Java示例并满足完整模式推断和完全非简化并行操作的目标的答案。或者,如果不可能的话,请采用最接近的解决方法。

我当前正在使用Spark 2.4.0。

我研究了以下相关问题:

Implicit schema discovery on a JSON-formatted Spark DataFrame column

这个问题类似于我的,尽管对于Scala而言。没有可接受的答案。 OP在评论中宣布,他们找到了使from_schema正常工作的“骇客”解决方案。解决方案的问题在于,除了“ hackiness”之外,它还只能从数据帧的第一行推断模式,因此类型可能过于严格:

val jsonSchema: String = df.select(schema_of_json(df.select(col("custom")).first.getString(0))).as[String].first

编辑:我尝试了以下注释中讨论的指示here的解决方案。这是实现:

    SparkSession spark = SparkSession
            .builder()
            .appName("example")
            .master("local[*]")
            .getOrCreate();

    Dataset<Row> df = spark.read().text(conf.getSourcePath());

    df.cache();

    String schema = df.select(schema_of_json(col("value")))
          .as(Encoders.STRING())
          .first();
    df.withColumn("parsedJson", from_json(col("value"), schema, new HashMap<String, String>()))
            .drop("value")
            .write()
            .mode("append")
            .parquet(conf.getDestinationPath());

从这段代码中我得到了一个错误:

AnalysisException: cannot resolve 'schemaofjson(`value`)' due to data type mismatch: The input json should be a string literal and not null; however, got `value`.;;
'Project [schemaofjson(value#0) AS schemaOfjson(value)#20]
+- Relation[value#0] text

此错误将我引向以下Spark Pull请求: https://github.com/apache/spark/pull/22775

似乎表明schema_of_json从未打算应用于整个表以对整个事物进行模式推断,而是从直接使用传入的单个文字JSON样本推断模式lit("some json")。在那种情况下,我不知道Spark为从整个表上的JSON进行完整模式推断提供任何解决方案。除非这里的某人可以更正我对此请求请求的阅读或提供其他方法?

1 个答案:

答案 0 :(得分:0)

使用DataFrameReader.json(Dataset<String>)实际上有一个非常简单的解决方案,不知道为什么它没有出现在我的搜索中:

    Dataset<String> ds = ...;

    spark.read()
        .json(ds)
        .write()
        .mode("append")
        .parquet(conf.getDestinationPath());

如果源数据集中有多个列,显然您可以只选择要操作的一列。内容类型必须为String(例如,不能为Row)。