繁体   English   中英

使用 Play JSON 查找具有动态键的 JSON 元素的路径

[英]Find the path of an JSON element with dynamic key with Play JSON

我正在使用带有 Scala 的 Play 框架。 我有以下 JSON 结构:

{
    "a": 1540554574847,
    "b": 2,
    "c": {
    "pep3lpnp1n1ugmex5uevekg5k20wkfq3": {
    "a": 1,
    "b": 1,
    "c": 1,
    "d": 1
    },
    "p3zgudnf7tzqvt50g7lpr2ryno7yugmy": {
    "b": [
    "d10e5600d11e5517"
    ],
    "c": 1,
    "d": 1,
    "e": 1,
    "g": 1,
    "h": [
    "d10e5600d11e5517",
    "d10e5615d11e5527",
    "d10e5605d11e5520",
    "d10e5610d11e5523",
    "d10e5620d11e5530"
    ],
    "q": "a_z6smu56gstysjpqbzp21ruxii6g2ph00"
    },
    "33qfthhugr36f5ts4251glpqx0o373pe": {
    "b": [
    "d10e5633d11e5536"
    ],
    "c": 1,
    "d": 1,
    "e": 1,
    "g": 1,
    "h": [
    "d10e5638d11e5539",
    "d10e5633d11e5536",
    "d10e5643d11e5542",
    "d10e5653d11e5549",
    "d10e5648d11e5546"
    ],
    "q": "a_cydo6wu1ds340j3q6qxeig97thocttsp"
    }
    }
    }

我需要从路径"c" -> "pep3lpnp1n1ugmex5uevekg5k20wkfq3" -> "b" , "c" -> "p3zgudnf7tzqvt50g7lpr2ryno7yugmy" -> "b" , "c" -> "33qfthhugr36f5ts4251glpqx0o373pe" -> "b" ,依此类推,其中"pep3lpnp1n1ugmex5uevekg5k20wkfq3"是动态的,并且会随着每个 JSON 输入而变化。

输出应该像 Seq(object(q,b,c))。

如果您不需要知道哪个生成的键属于哪个值,您可以使用递归路径\\\\运算符:

import play.api.libs.json.Json
import play.api.libs.json._

val jsonText = """{
   "a":1540554574847,
   "b":2,
   "c":{
      "onegeneratedkey":{
         "a":1,
         "b":1,
         "c":1,
         "d":1
      },
      "secondsonegeneratedkey":{
         "a":1,
         "b": [1, 2, 3],
         "c":1,
         "d":1
      }
   }
}"""

val result: Seq[JsValue] = Json.parse(jsonText) \ "c" \\ "b"
// res: List(1, [1,2,3])

更新。

要使用生成的键获取存储在对象中的所有值,可以使用JsObject#values

val valuesSeq: Seq[JsValue] = (Json.parse(jsonText) \ "c").toOption // get 'c' field
  .collect {case o: JsObject => o.values.toSeq} // get all object that corresponds to generated keys
  .getOrElse(Seq.empty)
// res: Seq({"a":1,"b":1,"c":1,"d":1}, {"a":1,"b":[1,2,3],"c":1,"d":1})

val valuesABC = valuesSeq.map(it => (it \ "a", it \ "b", it \ "c"))
// res: Seq((JsDefined(1),JsDefined(1),JsDefined(1)), (JsDefined(1),JsDefined([1,2,3]),JsDefined(1)))

我误读了这个问题,这是修改后的版本。

在这里,我使用 json.pick 读取 JsObject 并从那里迭代键。

Ps:您不必创建 Reads 或 case 类,但它应该使调用程序更具可读性。

import play.api.libs.json.Json
import play.api.libs.json._

val jsonText =
  """{
    "top": {
      "level2a": {
        "a": 1,
        "b": 1,
        "c": 1,
        "d": 1
      },
      "level2b": {
        "a": 2,
        "b": 2,
        "nested": {
          "b": "not interested"
        }
      }
    }
  }"""

case class Data(k: String, v: Int)
case class Datas(list: Seq[Data])

object Datas {
  implicit val reads: Reads[Datas] = (__ \ "top").json.pick.map {
    case obj: JsObject =>
      new Datas(obj.keys.flatMap(k => (obj \ k \ "b").validate[Int] match {
        case JsSuccess(v, _) => Some(Data(k, v))
        case _ => None
      }).toSeq)
  }
}

Json.parse(jsonText).validate[Datas].asOpt match {
  case Some(d) => println(s"found: $d")
  case _ => println("not found")
}

要反序列化 level2 内的内部结构,您可以选择创建内部结构并使用 Json.reads 创建默认读取。 只要数据结构是已知且可预测的。

例如

case class Internal(a: Int, b: Int, c: Option[Int], d: Option[Int])
object Internal {
  implicit val reads = Json.reads[Internal]
}
case class Data(k: String, v: Internal)
case class Datas(list: Seq[Data])
object Datas {
  implicit val reads: Reads[Datas] = (__ \ "top").json.pick.map {
    case obj: JsObject =>
      new Datas(obj.keys.flatMap(k => (obj \ k).validate[Internal].asOpt
        .map(v => Data(k, v))).toSeq)

  }
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM