简体   繁体   English

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

[英]Play framework (Scala) - Getting subset of json that contains arrays

I have many very large json-objects that I return from Play Framework with Scala. 我有很多非常大的json对象,它们是从Scala的Play Framework返回的。

In most cases the user doesn't need all the data in the objects, only a few fields. 在大多数情况下,用户不需要对象中的所有数据,仅需要几个字段。 So I want to pass in the paths I need (as query parameters), and return a subset of the json object. 所以我想传递我需要的路径(作为查询参数),并返回json对象的子集。

I have looked at using JSON Transformers for this task. 我已经考虑过使用JSON Transformers来完成此任务。

Filter code 筛选代码

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
    }
}

Usage: 用法:

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"}}

Problem 问题

This code works fine for JsObjects . 这段代码对JsObjects很好用。 But I can't make it work if there are JsArrays in the strucure. 但是,如果结构中包含JsArrays ,则无法使其工作。 I had hoped that my JsPath could contain an index to look up the field, but that's not the case. 我曾希望我的JsPath可以包含一个索引来查找字段,但事实并非如此。 (Don't know why I assumed that, maybe my head was too far in the JavaScript-world) (不知道为什么会这样,也许我的头在JavaScript世界中过高了)

So this would fail to return the first entry in the Array: 因此,这将无法返回Array中的第一个条目:

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

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

Question

My question is: How can I return a subset of a json structure that contains arrays? 我的问题是: 如何返回包含数组的json结构的子集?

Alternative solution 替代解决方案

I have the data as a case class first, and I serialize it to Json, and then run filterByPaths on it. 我首先将数据作为案例类,然后将其序列化为Json,然后在filterByPaths上运行filterByPaths Having a Reader that only creates the json I need in the first place might be a better solution, but creating a Reader on the fly, with configuration from queryparams seamed a more difficult task, then just stripping down the json afterwards. 首先拥有一个仅创建我需要的json的Reader可能是一个更好的解决方案,但是通过使用queryparams进行配置来动态创建一个Reader带来一项更加艰巨的任务,然后再剥离json。

The example of the returning array element: 返回数组元素的示例:

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

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

More about this in the Play Framework documentation: https://www.playframework.com/documentation/2.6.x/ScalaJson 有关Play框架文档的更多信息,请访问: https : //www.playframework.com/documentation/2.6.x/ScalaJson

Update 更新

It looks like you got the old issue RuntimeException: expected KeyPathNode . 看来您遇到了旧问题RuntimeException: expected KeyPathNode JsPath.json.put , JsPath.json.update can't past an object to a nesting array. JsPath.json.putJsPath.json.update不能将对象传递到嵌套数组。

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

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

What you can do: 你可以做什么:

  1. Use the JSZipper: https://github.com/mandubian/play-json-zipper 使用JSZipper: https : //github.com/mandubian/play-json-zipper
  2. Create a script to update arrays "manually" 创建脚本以“手动”更新阵列
  3. If you can afford it, strip array in a resulting object 如果负担得起,则在结果对象中剥离数组

Example of stripping array (point 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"}}

The example 这个例子

  1. The best to handle JSON objects is by using case classes and create implicit Reads and Writes, by that you can handle errors every fields directly. 处理JSON对象的最佳方法是使用案例类并创建隐式的读写操作,这样您就可以直接处理每个字段的错误。 Don't make it complicated. 不要复杂。

  2. Don't use .get() much recommended to use .getOrElse() because scala is a type-safe programming language. 不要使用.get()多推荐使用.getOrElse()因为Scala是一种安全的编程语言。

  3. Don't just use any Libraries except you know the process behind it, much better to create your own parsing method with simplified solution to save memory. 不要只使用任何库,除非您知道其背后的过程,否则最好使用简化的解决方案创建自己的解析方法以节省内存。

I hope it will help you.. 希望对您有帮助。

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

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