使用 play Json 库(或任何其他建议)对多级数组进行 JsLookup

Posted

技术标签:

【中文标题】使用 play Json 库(或任何其他建议)对多级数组进行 JsLookup【英文标题】:JsLookup on multiple level array using play Json library (or any other suggestion) 【发布时间】:2021-12-22 05:05:07 【问题描述】:

我的函数正在接收JsValue,现在这个json有列表,这个列表元素也可以是列表,例如:


  "firstName": "Elon",
  "lastName": "Musk",
  "companies": [
    
      "name": "Tesla",
      "city": "San Francisco",
      "offices": ["sf", "ny"],
      "management": 
        "loscation": "New York",
        "boardMembers": [
          
            "name": "John",
            "age": 45
          ,
          
            "name": "Mike",
            "age": 55
          ,
          
            "name": "Rebecca",
            "age": 35
          
        ]
      
    ,
    
      "name": "SpaceX",
      "city": "San Francisco",
      "offices": ["la", "ta"],
      "management": 
        "loscation": "San Mateo",
        "boardMembers": [
          
            "name": "Lisa",
            "age": 45
          ,
          
            "name": "Dan",
            "age": 55
          ,
          
            "name": "Henry",
            "age": 35
          
        ]
      
    
  ]

所以一个公司有一个管理对象,它有 boardMembers 列表。

我的函数正在接收这个元素的路径,例如:

companies[*].management.boardMembers[*].name

我希望它返回一个包含该对象所有元素的列表,因此结果将是:

["John", "Mike", "Rebecca", "Lisa", "Dan", "Henry"]

这有点复杂,但我想play.api.libs.json._ 可能会有一些可以提供帮助的功能。

考虑将其拆分pathStr.split("(\\[\\*]\\.|\\[\\*])").toList,然后迭代以获取所有元素的某种方式并返回 JsLookupResult 但不确定如何。

澄清一下:

我的方法将接收 2 个参数,JsValue 和字符串形式的路径 def myFunc(json: JsValue, path: String)

每次我调用myFunc 时,它都会收到不同的路径,我不确定只有在调用myFunc 之后它会是什么。

【问题讨论】:

【参考方案1】:

你可以这样做:

val jsPath = JsPath \ "companies" \\ "management" \ "boardMembers" \\ "name"
val result = jsPath(Json.parse(input))
println(result)

这将打印预期的输出。请参阅Scastie 示例。

请注意\\\的区别:

\ 寻找直系子女 \\ 寻找递归子节点

要实现myFunc,您可以尝试以下操作:

def findAllValuesAtPath(jsValue: JsValue, path: String): List[JsValue] = 
  val jsPath = JsPath(path
    .split("\\[\\*]\\.")
    .flatMap(s => s.split("\\.")
      .map(RecursiveSearch)
    ).toList)
  println(jsPath.path)
  jsPath(jsValue)

Here 是另一个 scastie。

【讨论】:

感谢汤姆!这适用于大多数情况,但如果我只想引用一个简单的数组,例如: "person": "kids": [ "jack", "rose" ] 路径:“person.kids”你会得到List(["jack","rose"]),你有什么建议吗?我希望得到像List("jack","rose") 这样的数组 当然!公认。但它并不是一个真正不同的问题伙伴,我的意思是将它作为一个场景包含在这个问题中。你说得对,因为我没有正确描述它。我只想让这个函数处理具有特定成员的复杂 obj 列表的路径(这已经与这个建议一起工作)或者指向一个简单列表的路径并返回这个列表......@Tomer Shetah 我的朋友有什么可以捕捉到这种情况的编辑建议吗? 我想发布另一个问题,但它将完全脱离上下文,它应该是其中的一部分,因为它要求的完整行为..@Tomer Shetah 上传了一个新问题,如果你不介意***.com/questions/71220791/…【参考方案2】:

正如您在文档中看到的,您可以使用 Reads 类型类来定义从 JSON 解码类型的方式。

import play.api.libs.json._

val input = """
  "firstName": "Elon",
  "lastName": "Musk",
  "companies": [
    
      "name": "Tesla",
      "city": "San Francisco",
      "offices": ["sf","ny"],
      "management": 
        "loscation": "New York",
        "boardMembers": [
          
            "name": "John",
            "age": 45
          ,
          
            "name": "Mike",
            "age": 55
          ,
          
            "name": "Rebecca",
            "age": 35
          
        ]
      
    ,
    
      "name": "SpaceX",
      "city": "San Francisco",
      "offices": ["la","ta"],
      "management": 
        "loscation": "San Mateo",
        "boardMembers": [
          
            "name": "Lisa",
            "age": 45
          ,
          
            "name": "Dan",
            "age": 55
          ,
          
            "name": "Henry",
            "age": 35
          
        ]
      
    
  ]
"""

val json = Json.parse(input)

// ---

case class BoardMember(name: String, age: Int)

implicit val br: Reads[BoardMember] = Json.reads

case class Company(boardMembers: Seq[BoardMember])

implicit val cr: Reads[Company] = Reads 
  case obj @ JsObject(_) =>
    (obj \ "management" \ "boardMembers").validate[Seq[BoardMember]].map 
      Company(_)
    

  case _ =>
    JsError("error.obj.expected")


val reads = Reads[Seq[String]] 
  case obj @ JsObject(_) =>
    (obj \ "companies").validate[Seq[Company]].map 
      _.flatMap(_.boardMembers.map(_.name))
    

  case _ =>
    JsError("error.obj.expected")


// ---

json.validate(reads)
//  play.api.libs.json.JsResult[Seq[String]] = JsSuccess(Vector(John, Mike, Rebecca, Lisa, Dan, Henry),)

【讨论】:

不我不知道路径,它被传递给我的函数,我提到的路径是一个例子......问题是,如果我得到这个路径,我怎么能返回该路径中所有列表的 1 个列表 我的函数将接收 json 作为 JsValue 和看起来像示例的路径,我需要返回该路径的所有元素的列表,就像我提到的示例响应 ["John", "Mike", "Rebecca", "Lisa", "Dan", "Henry"] 你必须知道路径 对不起,我知道路径,但每次调用方法时它都会改变 这意味着我每次都需要以不同的方式构建阅读器,而且我不使用案例类只有 jsValues 导致这个 json 可以改变

以上是关于使用 play Json 库(或任何其他建议)对多级数组进行 JsLookup的主要内容,如果未能解决你的问题,请参考以下文章

休眠双向多对多映射建议!

Tesseract 或任何其他 OCR 库

(Play 2.0) 为 AnyContent 设置最大 POST 大小

玩I18N库

使用 Play-JSON 库解析对象序列

如何解析JSON使用Play框架