简单问题:
如何使用play-json (2.3.x)对某些JsValue中的所有JsValue进行排序(递归)?
我的用法:
考虑一个在内部使用Set[String]的应用程序,当请求数据时,输出JSON将该集序列化为JSON数组。命令不重要。
现在,如果一个人想要编写一些测试来覆盖这个功能,因为条目的顺序并不重要(毕竟它是一个集合)。在内部和概念上),我只想检查所有应该返回的内容,我可能希望将响应JSON与我显式创建的“预期”JSON对象进行比较。
基于这个确切的原因,我想对JSON数组进行排序,并比较JsValue's。如何编写这样的转换器?
编辑:
我已经成功地编写了一个转换器来满足我的需求,但它不能对某些JsValue中的每个JsValue进行排序。我会在这里发布,因为它可能对其他人有用,但这不是我想要的。
val jsonSortTransformer = (__ \ 'fields).json.update(
Reads.JsObjectReads.map{
case JsObject(xs) => JsObject(
xs.map{
case (n,jv) => {
n -> (jv match {
case JsArray(arr) if arr.forall(_.isInstanceOf[JsString]) => JsArray(arr.sortBy(_.as[String]))
case _ => jv
})
}
}
)
}
)发布于 2014-11-12 18:27:37
您可以使用JsArray上的JsArray属性获取Seq[JsValue],然后任意排序,然后重新创建JsArray。例如:
scala> myJsArray
play.api.libs.json.JsArray = ["11","4","5","1","22","2"]
scala> JsArray(myJsArray.value.sortBy(_.as[JsString].value.toInt))
play.api.libs.json.JsArray = ["1","2","4","5","11","22"]如果您所做的只是比较您所知道的集合的实际值和期望值,那么您也可以对这两个属性使用value,构建一个Set并检查是否相等:
Set(actual.value: _*) == Set(expected.value: _*)或者把它们都分类:
val sortedSeq: JsArray => Seq[String] = array => array.value.map(_.toString).sorted
sortedSeq(actual) == sortedSeq(expected)要递归地对任意JsArrays中的所有JsValue进行排序,它可能如下所示:
def sortArrays(json: JsValue): JsValue = json match {
case JsObject(obj) => JsObject(obj.toMap.mapValues(sortArrays(_)).toList)
case JsArray(arr) => JsArray(arr.map(sortArrays).sortBy(_.toString))
case other => other
}
scala> myObj
play.api.libs.json.JsValue = {"a":[2,1],"b":[{"c":[3,2]},{"d":[4,3]}],"e":{"f":[5,4]}}
scala> sortArrays(myObj)
play.api.libs.json.JsValue = {"a":[1,2],"b":[{"c":[2,3]},{"d":[3,4]}],"e":{"f":[4,5]}}发布于 2017-08-20 15:46:04
恐怕本的回答是不正确的。
我将通过为Ordering s定义一个JsValue类来处理这个问题,然后使用它的比较方法来验证相等性(这意味着这实际上应该是一个object --而不是一个匿名类,如示例所示)。
我们不必使用Ordering,我只是觉得它比简单的compareTo方法更方便一些。当然,也可以将这个类/对象定义为implicit。
val jsonOrdering: Ordering[JsValue] = new Ordering[JsValue]() {
override def compare(x: JsValue, y: JsValue): Int = {
x.getClass.getName.compareTo(y.getClass.getName) match {
case 0 =>
(x, y) match {
case (JsNull, JsNull) => 0
case (JsString(valueX), JsString(valueY)) =>
valueX.compareTo(valueY)
case (JsNumber(valueX), JsNumber(valueY)) =>
valueX.compare(valueY)
case (JsBoolean(boolX), JsBoolean(boolY)) =>
boolX.compareTo(boolY)
case (JsArray(elementsX), JsArray(elementsY)) =>
elementsX.size.compareTo(elementsY.size) match {
case 0 =>
elementsX
// .sorted(this) // uncomment if array order DOES NOT matter
.zip(elementsY
// .sorted(this) // uncomment if array order DOES NOT matter
)
.view
.map {
case (elementX, elementY) => compare(elementX, elementY)
}
.find(_ != 0)
.getOrElse(0)
case nonZero => nonZero
}
case (JsObject(fieldsX), JsObject(fieldsY)) =>
fieldsX.size.compareTo(fieldsY.size) match {
case 0 =>
fieldsX.toSeq
.sortBy(_._1)
.zip(fieldsY.toSeq.sortBy(_._1))
.view
.flatMap {
case ((keyX, valueX), (keyY, valueY)) =>
Seq(keyX.compareTo(keyY), compare(valueX, valueY))
}
.find(_ != 0)
.getOrElse(0)
case nonZero => nonZero
}
}
case nonZero => nonZero
}
}我可能会将一些部分分割成私有/嵌套函数(这次我变懒了)。不管怎么说,让我们重温一下:
JsValue,从而递归地使用相同的方法),如果它们不相同,则返回它们的比较。
1. If no such pair exists, this means that the maps (objects) are the same (return 0).
请注意,尽管这种排序是一致的和确定性的,但它是相当任意的,并且没有传达太多的逻辑意义。
https://stackoverflow.com/questions/26889079
复制相似问题