我有很多非常大的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。
答案 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 KeyPathNode
。 JsPath.json.put
,JsPath.json.update
无法将对象粘贴到嵌套数组中。
https://github.com/playframework/playframework/issues/943
https://github.com/playframework/play-json/issues/82
您可以做什么:
剥离数组的示例(第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)
处理JSON对象的最佳方法是使用案例类并创建隐式读写,这样您就可以直接处理每个字段的错误。不要复杂。
不建议使用.get()
,因为scala是一种类型安全的编程语言,因此不建议使用.getOrElse()
。
不仅要使用任何库,除非您知道其背后的过程,否则最好使用简化的解决方案创建自己的解析方法以节省内存。
我希望它能对您有所帮助。