当前位置: 首页 > 知识库问答 >
问题:

使用 Spark 从 DynamoDB JSON 字符串中提取嵌套的 Json 字段?

常俊侠
2023-03-14

我正在从Spark读取一个dynamodb表,这个表在一个字段中有一个JSON字符串,在其他字段中有字符串。我能够读取JSON字段,但不能读取嵌套的JSON字段。这不是使用dataframes的查询Json列的副本。这个问题解释了如何从JSON字符串中提取列,但没有解释嵌套的JSON列。

import com.github.traviscrawford.spark.dynamodb._
val users = sqlContext.read.dynamodb("Dynamodb_table")

users.show(1)

示例数据集

 |col1                                                        | ID | field2|field3|
 -------------------------------------------------------------------------------------
 |{"a":[{"b":"value1","x":23},{"b":value2,"x":52}],"c":"valC"}|A1  | X1    |Y1    |

我需要从col1(JSON结构)和ID字段中提取几个字段。我能够理解如何解析JSON字段(col1)并从col1中获取字段‘c ’,就像这里解释的那样,但是不能提取嵌套字段。

我的代码:

val users = sqlContext.read.dynamodb("Dynamodb_table")
val data = users.selectExpr("get_json_object(col1, '$.c')","get_json_object(col1, '$.a')","ID")

data.show(1,false)
|a                                              |c   |ID|
---------------------------------------------------------
|[{"b":"value1","x":23},{"b":value2","x":52}...]|valC|A1|

现在,当我尝试在上面的数据帧上应用相同的get_json_object时,我得到所有的空值。

val nestedData = data.selectExpr("get_json_object(a, '$.b')","c","ID")
nestedData.show(false)

|get_json_object(a, '$.b')| c  | ID|
------------------------------------
|null                     |valC|A1 |    

我也尝试了爆炸,因为col 'a'有数组和结构。但这也不起作用,因为数据帧“data”将col/字段“a”作为字符串而不是数组返回。任何想法如何解决这个问题?

更新:我还尝试使用JSON4s和net.liftweb.json.parse进行解析。那也无济于事

case class aInfo(b: String) 
case class col1(a: Option[aInfo]), c: String)

import net.liftweb.json.parse
val parseJson = udf((data: String) => {
implicit val formats = net.liftweb.json.DefaultFormats
parse(data).extract[Data]
})

val parsed = users.withColumn("parsedJSON", parseJson($"data"))
parsed.show(1)

当我使用这些解析器时,所有值都显示为空。

我的预期结果:我试图从数据集中得到一个扁平的结构

|b     |x |c   | ID|
--------------------
|value1|23|valC|A1 |
|value2|52|valC|A1 |

共有1个答案

周玺
2023-03-14

我相信所有必需的拼图都已经在这里了,所以让我们一步一步地遵循这个步骤。您的数据相当于:

val df = Seq((
  """{"a":[{"b":"value1"},{"b": "value2"}],"c":"valC"}""", "A1", "X1", "Y1"
)).toDF("col1", "ID",  "field2", "field3")

Spark 提供了 json4s,它实现了与 Lift 相同的查询 API:

import org.json4s._
import org.json4s.jackson.JsonMethods._

例如,我们可以使用LINQ风格的API来定义UDF:

val getBs = udf((s: String) => for { 
  JString(b) <- parse(s) \ "a" \ "b" 
} yield b)

如果你想提取多个字段,你当然可以扩展它。例如,如果JSON字符串有多个字段

{"a":[{"b":"value1","d":1},{"b":"value2","d":2}],"c":"valC"}

你可以:

for  {
  JObject(a) <- parse(s) \ "a"
  JField("b", JString(b))  <- a
  JField("d", JInt(d))  <- a
} yield (b, d)

这假设两个字段都存在,否则不会有匹配。要处理丢失的字段,您可能更喜欢类似XPath的表达式或提取器:

case class A(b: Option[String], d: Option[Int])

(parse(s) \ "a").extract(Seq[A])

像这样的UDF可以与爆炸一起使用以提取字段:

val withBs = df.withColumn("b", explode(getBs($"col1")))

结果:

+--------------------+---+------+------+------+
|                col1| ID|field2|field3|     b|
+--------------------+---+------+------+------+
|{"a":[{"b":"value...| A1|    X1|    Y1|value1|
|{"a":[{"b":"value...| A1|    X1|    Y1|value2|
+--------------------+---+------+------+------+

您尝试使用Lift是不正确的,因为您希望<code>a。它应该是选项[Seq[aInfo]]

case class col1(a: Option[Seq[aInfo]], c: String)

使用这样定义的类,解析应该可以正常工作。

如果使用当前版本 (Spark 2.1.0),则 SPARK-17699 引入了一个from_json方法,该方法需要架构:

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

val bSchema = StructType(Seq(StructField("b", StringType, true)))
val aSchema = StructField("a", ArrayType(bSchema), true)
val cSchema = StructField("c", StringType, true)

val schema =  StructType(Seq(aSchema, cSchema))

并可应用为:

import org.apache.spark.sql.functions.from_json

val parsed = df.withColumn("col1", from_json($"col1", schema))

之后,您可以使用常用符号选择字段:

parsed.select($"col1.a.b")
 类似资料:
  • 我的数据框中有一列包含JSON列表,但类型是字符串。我需要在此列上运行,因此首先需要将其转换为列表。我找不到太多关于这个用例的参考。 示例数据: 上面是数据的样子,字段是不固定的(索引0可能有JSON和一些字段,而索引1将有一些字段和一些其他字段)。列表中可以有更多嵌套的JSONs或一些额外的字段。我现在正在用这个- <code>“分解(拆分(regexp_replace(regexp_repla

  • 我正在以JSON字符串的形式从数据库中获取数据: 如何从给定的JSON字符串中提取公司名称?

  • 我正在使用ApacheJMeter发出HTTP请求。 响应格式如下: HTTP/1.1 100继续 HTTP/1.1 200 OK x-powering-by: Express access-Control-low-source:*access-Control-low-method: HEAD, POST, PUT, GET, OPTIONS, DELETE access-Control-low-

  • 问题内容: 我正在使用select2中的multiselect元素来输入多个“标签”。当我想从元素中获取值时,我会得到如下信息(对于我在框中输入的tag1和tag2): 如何从数组中的文本中获取结果,如下所示: 而我该如何逆转这一过程呢? 问题答案: 试试这个简单的迭代。

  • 问题内容: 我有一个包含Json的字符串。看起来像这样: 我正在尝试保留“ img_url”。我已经安装了Json.NET,并且在这里发现了类似的问题。 例如这样的事情: 在我来说,我改变到,etc..no运气 现在这是我的代码: 在提取值之前,是否必须对字符串做一些事情?谢谢! 问题答案: 不是根对象的属性-它是对象的属性: 另外的选择:

  • 所谓字符串,指的就是字符的序列或者“串”。我们要在字符串上执行的第一个操作是提取所有字符中的一个。C++使用方括号([和])执行该操作: apstring fruit = "banana"; char letter = fruit[1]; cout << letter << endl; 表达式fruit[1]表明我们要从字符串变量fruit中取得编号为1的字符,并将结果保存在字符变量letter