鉴于以下JSON ......
scala> val js = Json.parse("""{"key1": "value1", "key2": "value2","list":[{"item1": "value1"},{"item2": "value2"}]}""")
js: play.api.libs.json.JsValue = {"key1":"value1","key2":"value2","list":[{"item1":"value1"},{"item2":"value2"}]}
...我得到list
的第一个元素:
scala> val l = (js \ "list").as[List[JsValue]]
l: List[play.api.libs.json.JsValue] = List({"item1":"value1"}, {"item2":"value2"})
scala> val first = l(0)
first: play.api.libs.json.JsValue = {"item1":"value1"}
...但是如何在给定索引处从list
中删除元素?
在JsValue
和JsPath
中没有任何内容,你可以使用像Monocle这样的镜头库。否则,这是一种方法:
(js \ "list").get match {
case JsArray(items) => dropAt(items, 1)
}
其中dropAt
是:
def dropAt[A](items: Seq[A], id: Int): Seq[A] =
items.zipWithIndex.filter(_._2 != id).map(_._1)
(dropAt
不漂亮,但我不知道有任何不错的API。)
标准集合库中没有dropAt
。您可以使用enrich-my-library模式添加一个。在丰富集合时,通常最好使用CanBuildFrom
结构,这样可以保持强大的类型。您可以将dropAt
实现为:
implicit class TraversableDropAt[A, Repr <: Traversable[A]](val xs: TraversableLike[A, Repr]) extends AnyVal {
def dropAt[That](n: Int)(implicit cbf: CanBuildFrom[Repr, A, That]): That = {
val bf = cbf()
bf.sizeHint(xs.size - 1)
bf ++= xs.take(n)
bf ++= xs.drop(n + 1)
bf.result
}
}
这将允许您为任何myCollection.dropAt(n)
扩展(例如Traversable
,List
,Seq
等)调用Iterable
。
当您使用PlayJSON类型时,您通常最好尽快将它们转换为普通的Scala类型。在这里,您可以通过以下几种方式将数组转换为Seq[JsValue]
:
val items = (js \ "list").as[Seq[JsValue]]
val items = (js \ "list").as[JsArray].value
val items = (js \ "list") match { case JsArray(items) => items }
val JsArray(items) = (js \ "list")
一旦你有items
集合,你可以使用新的dropAt
方法。
另一个选择是使用上面显示的enrich-my-library模式将dropAt
方法直接添加到JsArray
(甚至JsValue
)。
如果你需要转换回JsArray
,你可以使用Json.arr
方法。
您可以通过将元素转换为可变集合而不是不可变列表来删除元素。在可变集合上,您可以调用将修改集合的remove(int: Index)
操作。所以你的案子可以写成
val js = Json.parse("""{"key1": "value1", "key2": "value2","list":[{"item1": "value1"},{"item2": "value2"}]}""")
val l = (js \ "list").as[ArrayBuffer[JsValue]]
l.remove(0) // returns the removed element at index 0 but modifies the underlying collection l