circe的JSON遍历中的通配符路径
Wildcard path in circe's JSON traversal
考虑这个 JSON:
{
"myDocument": {
"variableName": {
"attr1": "val1",
"attr2": "val2"
}
}
}
我想处理 JSON 个具有某些共同部分的文档。具体来说,我想提取 "common" 部分,并且我想在这种情况下使用 circe。
我正在查看 JSON 遍历指南和 optics 因为我将处理深层嵌套结构。
在前面的例子中,有一个属性持有一个对象。我要解析的文档将具有不同名称的 variableName
属性,所以我不能这样做:
root.myDocument.variableName.attr1
有什么方法可以指定某种通配符吗?像这样:
root.myDocument.*.attr1
我无法控制该属性中存在的所有可能的名称组合,但我当然知道该属性将出现在每个文档中的确切位置。将 myDocument
视为固定属性,将 variableName
视为在每个文档中具有不同名称的固定属性。
我找不到关于此的任何信息。可以吗?
谢谢
知道 myDocument
的第一个子元素是变量元素,并且您只想要 myDocument
第一个子元素的 attr1
和 attr2
个子元素,您可以执行以下操作:
import io.circe.Decoder
import io.circe._
import io.circe.jawn._
val in2 =
"""
|[
| {
| "myDocument": {
| "firstUnknown": {
| "attr1": "val1",
| "ignored": "not_important",
| "attr2": "val2"
| }
| }
| },
| {
| "myDocument": {
| "secondUnknown": {
| "also_ignored": "not_important",
| "attr1": "val3",
| "attr2": "val4"
| }
| }
| },
| {
| "myDocument": {
| "thirdUnknown": {
| "attr1": "val5",
| "ignored2": "not_important",
| "attr2": "val6",
| "again_ignored": "not_important"
| }
| }
| }
|]
""".stripMargin
case class MyDocument(attr1 : String, attr2 : String)
object MyDocument{
implicit val decoder : Decoder[MyDocument] = Decoder.instance{hCursor =>
val firstChildKey = hCursor.downField("myDocument").keys.flatMap(_.headOption)
firstChildKey.map{key =>
for{
attr1 <- hCursor.downField("myDocument").downField(key).downField("attr1").as[String]
attr2 <- hCursor.downField("myDocument").downField(key).downField("attr2").as[String]
} yield MyDocument(attr1,attr2)
}.getOrElse(Left(DecodingFailure("Element is not a Json object or it's empty.",List())))
}
}
val docs = parse(in2).flatMap(_.as[Vector[MyDocument]])
想法是,一旦我们将光标移动到 myDocument
元素,我们就会得到 json 对象包含的所有 keys
,取第一个(它将是我们感兴趣的变量元素的键),然后用它来将光标移动到所需的位置以读取attr1
和attr2
。
最后一行的结果是:
Right(Vector(MyDocument(val1,val2), MyDocument(val3,val4), MyDocument(val5,val6)))
考虑这个 JSON:
{
"myDocument": {
"variableName": {
"attr1": "val1",
"attr2": "val2"
}
}
}
我想处理 JSON 个具有某些共同部分的文档。具体来说,我想提取 "common" 部分,并且我想在这种情况下使用 circe。 我正在查看 JSON 遍历指南和 optics 因为我将处理深层嵌套结构。
在前面的例子中,有一个属性持有一个对象。我要解析的文档将具有不同名称的 variableName
属性,所以我不能这样做:
root.myDocument.variableName.attr1
有什么方法可以指定某种通配符吗?像这样:
root.myDocument.*.attr1
我无法控制该属性中存在的所有可能的名称组合,但我当然知道该属性将出现在每个文档中的确切位置。将 myDocument
视为固定属性,将 variableName
视为在每个文档中具有不同名称的固定属性。
我找不到关于此的任何信息。可以吗?
谢谢
知道 myDocument
的第一个子元素是变量元素,并且您只想要 myDocument
第一个子元素的 attr1
和 attr2
个子元素,您可以执行以下操作:
import io.circe.Decoder
import io.circe._
import io.circe.jawn._
val in2 =
"""
|[
| {
| "myDocument": {
| "firstUnknown": {
| "attr1": "val1",
| "ignored": "not_important",
| "attr2": "val2"
| }
| }
| },
| {
| "myDocument": {
| "secondUnknown": {
| "also_ignored": "not_important",
| "attr1": "val3",
| "attr2": "val4"
| }
| }
| },
| {
| "myDocument": {
| "thirdUnknown": {
| "attr1": "val5",
| "ignored2": "not_important",
| "attr2": "val6",
| "again_ignored": "not_important"
| }
| }
| }
|]
""".stripMargin
case class MyDocument(attr1 : String, attr2 : String)
object MyDocument{
implicit val decoder : Decoder[MyDocument] = Decoder.instance{hCursor =>
val firstChildKey = hCursor.downField("myDocument").keys.flatMap(_.headOption)
firstChildKey.map{key =>
for{
attr1 <- hCursor.downField("myDocument").downField(key).downField("attr1").as[String]
attr2 <- hCursor.downField("myDocument").downField(key).downField("attr2").as[String]
} yield MyDocument(attr1,attr2)
}.getOrElse(Left(DecodingFailure("Element is not a Json object or it's empty.",List())))
}
}
val docs = parse(in2).flatMap(_.as[Vector[MyDocument]])
想法是,一旦我们将光标移动到 myDocument
元素,我们就会得到 json 对象包含的所有 keys
,取第一个(它将是我们感兴趣的变量元素的键),然后用它来将光标移动到所需的位置以读取attr1
和attr2
。
最后一行的结果是:
Right(Vector(MyDocument(val1,val2), MyDocument(val3,val4), MyDocument(val5,val6)))