让我们假设以下两个JSON片段之一:
{ "include": ["field1", "field2", "fieldN"] }
{ "exclude": ["field1", "field2", "fieldN"] }
我需要像这样转换include
数组......
{ "field1": 1, "field2": 1, "fieldN": 1 }
...和这样的exclude
数组:
{ "field1": 0, "field2": 0, "fieldN": 0 }
[仅供参考:我需要将输入JSON转换为Mongo的预测。]
以下是我目前的解决方案 - 我已将其实施为JsValue
扩展程序:
object testTypeExtensions {
implicit class TestJsExtensions(val json: JsValue) extends AnyVal {
def toProjection: JsValue = {
if (json \\ "include" nonEmpty)
JsObject(for (field <- (json \ "include").as[List[JsString]])
yield (field.value, JsNumber(1)))
else if (json \\ "exclude" nonEmpty)
JsObject(for (field <- (json \ "exclude").as[List[JsString]])
yield (field.value, JsNumber(0)))
else Json.obj()
}
}
}
上面的代码有效......
scala> val p = Json.obj("exclude" -> Json.arr("field1", "field2"))
p: play.api.libs.json.JsObject = {"exclude":["field1","field2"]}
scala> p.toProjection
res12: play.api.libs.json.JsObject = {"field1":0,"field2":0}
...但我确信使用JsZipper
可以更好地写出来。
此外它不是很灵活,因为它只管理include
和exclude
键,而我还想管理其他类似的情况,如排序对象:
{ "asc": ["field1", "field2"] }
{ "desc": ["field1", "field2"] }
...转化为......
{ "field1": 1, "field2": 1 }
...和
{ "field1": -1, "field2": -1 }
那就是说,我想到的是一个管理任何类型的命名JSON数组的通用方法,如:
object testypeExtensions {
implicit class TempJsExtensions(val json: JsValue) extends AnyVal {
def namedArrayToObject(keys: String*): JsValue = {
// how to implement it, possibly with JsZipper
}
}
}
namedArrayToObject
方法应在当前JSON中搜索指定的keys
,并为第一次匹配生成一个对象,就像我在本文开头所描述的那样,可能是JsZipper
。这是对预期结果的模拟。
搜索exclude
和include
并将第一场比赛作为JsObject
返回:
scala> val p = Json.obj("exclude" -> Json.arr("field1", "field2"))
scala> p.namedArrayToObject("exclude", "include")
res12: play.api.libs.json.JsObject = {"field1":0,"field2":0}
与之前相同......但现在输入JSON包含include
而不是exclude
:
scala> val p = Json.obj("include" -> Json.arr("field1", "field2"))
scala> p.namedArrayToObject("exclude", "include")
res12: play.api.libs.json.JsObject = {"field1":1,"field2":1}
搜索asc
和desc
并将第一场比赛作为JsObject
返回:
scala> val p = Json.obj("desc" -> Json.arr("field1", "field2"))
scala> p.namedArrayToObject("asc", "desc")
res12: play.api.libs.json.JsObject = {"field1":-1,"field2":-1}
......等等。
如果没有匹配项,namedArrayToObject
应返回空JsObject
。任何关于如何以正确的方式实现这一点的建议都将非常感激。
答案 0 :(得分:1)
您可以使用JSON transformations非常直接地执行此操作:
import play.api.libs.json._
def toObj(value: Int) = Reads.of[List[String]].map(
keys => Json.toJson(keys.map(_ -> value).toMap)
)
val transformation =
(__ \ 'include).json.update(toObj(1)) andThen
(__ \ 'exclude).json.update(toObj(0))
我们可以定义一个示例对象并应用我们的转换:
val example = Json.parse("""{
"include": ["field1", "field2", "field3"],
"exclude": ["field4", "field5", "field6"]
}""")
val transformed = example.transform(transformation)
然后:
scala> transformed.foreach(Json.prettyPrint _ andThen println)
{
"include" : {
"field1" : 1,
"field2" : 1,
"field3" : 1
},
"exclude" : {
"field4" : 0,
"field5" : 0,
"field6" : 0
}
}
这与您想要的API不完全匹配,但它应该很容易适应,我建议远离隐式类业务,无论如何 - 它的组合性要低得多,并且处理无效输入不那么优雅。
答案 1 :(得分:0)
特拉维斯帮助我找到了方法......下面最终我的解决方案完全符合我的要求:
object testExtensions {
implicit class testJsExtensions(val json: JsValue) extends AnyVal {
def toParams(pairs: (String, Int)*): JsValue = {
for (pair <- pairs) { json.getOpt(__ \ pair._1).map { values =>
JsObject(for (field <- values.as[List[JsString]])
yield (field.value, JsNumber(pair._2)))
} match {
case Some(params) => return params
case _ =>
}}
Json.obj()
}
}
}
scala> example.toParams(("include", 1), ("exclude", 0))
res63: play.api.libs.json.JsValue = {"field1":1,"field2":1,"field3":1}
scala> example.toParams(("exclude", 0))
res64: play.api.libs.json.JsValue = {"field4":0,"field5":0,"field6":0}
再次感谢特拉维斯: - )