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 第一个子元素的 attr1attr2 个子元素,您可以执行以下操作:

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,取第一个(它将是我们感兴趣的变量元素的键),然后用它来将光标移动到所需的位置以读取attr1attr2

最后一行的结果是:

Right(Vector(MyDocument(val1,val2), MyDocument(val3,val4), MyDocument(val5,val6)))