播放框架(Scala)-获取包含数组的json子集

时间:2019-01-17 09:18:56

标签: json scala playframework

我有很多非常大的json对象,这些对象是通过Scala从Play Framework返回的。

在大多数情况下,用户不需要对象中的所有数据,仅需要几个字段。因此,我想传递所需的路径(作为查询参数),并返回json对象的子集。

我已经考虑过使用JSON Transformers来完成这项任务。

过滤器代码

def filterByPaths(paths: List[JsPath], inputObject: JsObject) : JsObject = {
  paths
    .map(_.json.pick)
    .map(inputObject.transform)
    .filter(_.isSuccess)
    .map { case JsSuccess(value, path) => (value, path) }
    .foldLeft(Json.obj()) { (obj, jsValueAndPath) =>
      val(jsValue, path) = jsValueAndPath
      val transformer = __.json.update(path.json.put(jsValue))
      obj.transform(transformer).get
    }
}

用法:

val input = Json.obj(
  "field1" -> Json.obj(
    "field2" -> "right result"
  ),
  "field4" -> Json.obj(
    "field5" -> "not included"
  ),
)

val result = filterByPaths(List(JsPath \ "field1" \ "field2"), input)
// {"field1":{"field2":"right result"}}

问题

此代码适用于JsObjects。但是,如果结构中包含JsArrays,则无法使其正常工作。我曾希望JsPath可以包含一个索引来查找该字段,但事实并非如此。 (不知道为什么会这样,也许我的头在JavaScript世界中太遥远了)

因此,这将无法返回数组中的第一个条目:

val input: JsObject = Json.parse("""
  {
    "arr1" : [{
      "field1" : "value1"
    }]
  }
  """).as[JsObject]

val result = filterByPaths(List(JsPath \ "arr1" \ "0"), input)
// {}

问题

我的问题是:如何返回包含数组的json结构的子集?

替代解决方案

我首先将数据作为案例类,然后将其序列化为Json,然后在其上运行filterByPaths。首先让Reader只创建我需要的json可能是一个更好的解决方案,但通过使用queryparams进行配置来动态创建Reader则是一项比较困难的任务,然后剥离然后是json。

2 个答案:

答案 0 :(得分:2)

返回数组元素的示例:

val input: JsValue = Json.parse("""
  {
    "arr1" : [{
      "field1" : "value1"
    }]
  }
  """)

val firstElement = (input \ "arr1" \ 0).get
val firstElementAnotherWay = input("arr1")(0) 

有关Play框架文档的更多信息:https://www.playframework.com/documentation/2.6.x/ScalaJson

更新

您似乎遇到了旧问题RuntimeException: expected KeyPathNodeJsPath.json.putJsPath.json.update无法将对象粘贴到嵌套数组中。

https://github.com/playframework/playframework/issues/943

https://github.com/playframework/play-json/issues/82

您可以做什么:

  1. 使用JSZipper:https://github.com/mandubian/play-json-zipper
  2. 创建脚本以“手动”更新数组
  3. 如果负担得起,则在结果对象中剥离数组

剥离数组的示例(第3点):

def filterByPaths(paths: List[JsPath], inputObject: JsObject) : JsObject = {
  paths
    .map(_.json.pick)
    .map(inputObject.transform)
    .filter(_.isSuccess)
    .map { case JsSuccess(value, path) => (value, path)}
    .foldLeft(Json.obj()) { (obj, jsValueAndPath) =>
      val (jsValue, path) = jsValueAndPath
      val arrayStrippedPath = JsPath(path.path.filter(n => !(n.toJsonString matches """\[\d+\]""")))
      val transformer = __.json.update(arrayStrippedPath.json.put(jsValue))
      obj.transform(transformer).get
    }
}

val result = filterByPaths(List(JsPath \ "arr1" \ "0"), input)
// {"arr1":{"field1":"value1"}}

示例

答案 1 :(得分:1)

  1. 处理JSON对象的最佳方法是使用案例类并创建隐式读写,这样您就可以直接处理每个字段的错误。不要复杂。

  2. 不建议使用.get(),因为scala是一种类型安全的编程语言,因此不建议使用.getOrElse()

  3. 不仅要使用任何库,除非您知道其背后的过程,否则最好使用简化的解决方案创建自己的解析方法以节省内存。

我希望它能对您有所帮助。