get_json_object与基于行的路径?

时间:2018-06-28 08:30:42

标签: python apache-spark pyspark apache-spark-sql

我有一张这样的桌子:

row  | key   |  json
0    | a     |  {'something':{'a':1}}
1    | b     |  {'something':{'a':2, 'b':3 }}

我想在key列中定义的json中检索值:

row | value
0   | 1 // from $.something.a
1   | 3 // from $.something.b

在蜂巢上可以做

select get_json_object(json, concat("$.something.", key) from table;

,它将返回正确的值。但是,在pyspark上,我找不到复制此行为的方法,它似乎无法基于该列构建路径。我尝试过

context.table("table").select(
    get_json_object(F.col("json", concat("$something.", F.col("key"))
);

它告诉我“列不可迭代”。但是,仅使用concat可以正常工作:

context.table("table")
 .select(
    concat("$something.", F.col("key").alias(path)
 );

row | path
0   | $.something.a
1   | $.something.b

我能够使用UDF做到这一点,但是有什么方法可以立即使用PySpark完成它?

1 个答案:

答案 0 :(得分:1)

当前的spark实现无法提供将第二个参数作为get_json_object中的列传递的方法(尽管在内部它会将其转换为文字列并使用它。)

我使用UDF实现相同的目的:

我有输入的JSON,而我感兴趣的字段的路径(每个JSON可能不同)来自另一个文件。

创建了一个UDF,它使用两个参数(JSON列和path列)并以JSON形式返回该路径处的字符串值。 `

def getValueFromJson = (json: String, path: String) => {

    def getValue(json: JsonNode, strings: Array[String], index: Int): String = {
      if (index < strings.length - 1)
        getValue(json.path(strings(index).trim), strings, index + 1) //1 //0
      else
        json.path(strings(index).trim).toString
    }

    val mapper = new ObjectMapper()
    val root: JsonNode = mapper.readTree(json) //c.b
    val strings = path.split('.')
    val value = getValue(root, strings, 0)
    value
  }

UDF的用法:

    val a = "{ \"id\": 1, \"b\": \"in 1\" }"
    val b = "{ \"id\": 2, \"c\": {  \"b\": \"in 2\" } }"
    val c = "{ \"id\": 3, \"b\": \"in 3\" }"
    val d = "{ \"id\": 4, \"f\": {  \"a\": \"in 4\" } }"

    val ll = List(a, b, c, d)
    val path = spark.read.format("com.databricks.spark.csv").option("inferSchema", "true").option("header", "true").load("paths.csv")

    import spark.implicits._
    val ip = spark.sparkContext.parallelize(ll).toDF

    val ipWithId = ip.withColumn("id", get_json_object(col("value"), "$.id").cast("integer"))

    val joined = ipWithId.join(path, Seq("id"))

    val getValFromJson = spark.udf.register("getjson", getValueFromJson)

    val finalDf = joined.withColumn("valfromJson", getValFromJson(col("value"), col("path")))

    finalDf.show(false)

Path.csv在哪里:

id,path
1, b
2, c.b
3, b
4, f.a

这是输出:

+---+----------------------------------+----+-----------+
|id |value                             |path|valfromJson|
+---+----------------------------------+----+-----------+
|1  |{ "id": 1, "b": "in 1" }          | b  |"in 1"     |
|2  |{ "id": 2, "c": {  "b": "in 2" } }| c.b|"in 2"     |
|3  |{ "id": 3, "b": "in 3" }          | b  |"in 3"     |
|4  |{ "id": 4, "f": {  "a": "in 4" } }| f.a|"in 4"     |
+---+----------------------------------+----+-----------+