使用Play JSON查找具有动态键的JSON元素的路径
我正在使用Scala的Play框架。我有以下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"
{
"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)。。。。。