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

使用Play JSON查找具有动态键的JSON元素的路径,json,scala,playframework,Json,Scala,Playframework,我正在使用Scala的Play框架。我有以下JSON结构: { "a": 1540554574847, "b": 2, "c": { "pep3lpnp1n1ugmex5uevekg5k20wkfq3": { "a": 1, "b": 1, "c": 1, "d": 1 }, "p3zgudnf7tzqvt50g7lpr2ryno7yugmy": { "b": [ "d10e5600d11e5517"

我正在使用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”->“PEP3LPNP1N1UGMEX5UEVEKG20WKFQ3”->“b”
“c”->“p3zgudnf7tzqvt50g7lpr2ryno7yugmy”->“b”
“c”->“33qfthhugr36f5ts4251glpqx0o373pe”->“b”
等等,其中
“PEP3LPNP1N1UGMEX5UEVEKG20WKFQ3”
是动态的,每个JSON输入都会发生变化


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

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

在这里,我使用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)

  }
}

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

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])
UPD

要使用生成的键获取对象中存储的所有值,可以使用
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)))

在您的情况下,以下输入的输出是什么?输出应该类似于Seq(object(q,b,c))。谢谢,但我将如何拥有currentFieldName,因为在同一级别上有多个键,并且它是动态的。我需要该级别的所有值。是否可以同时读取c和d并存储到数据对象中?@RahulJ您的数据示例中的“b”可能具有不同的类型。这意味着您必须进行多态反序列化。i、 e.取决于反序列化为StringArrayInternal或IntInternal的数据类型。在调用者中,使用match语句来区分这些参数。在返回seq(“b”)时,是否有任何方法可以使用这种方式获取a、b、c参数?我还需要值映射。(a,b,c)(a,b,c)。。。。。