JSON 为 Java 列表写入

JSON Writes for Java List

我正在使用 Scala 的 Play Framework 开发后端服务器。但是,我正在调用一个外部库(用 java 编写)returns 一个 Java 列表 (util.List)。我为列表中包含的对象创建了一个写入,但是我不知道如何为实际列表编写写入以便它可以是通用的(不需要为列表和列表,仅写入 A 和 B)。

我知道我可以使用 JavaConversions 将 Java 列表转换为 Scala Seq(已经实现了 Writes),但由于速度至关重要,我不想这样做额外转换。

您可以编写一个 Writes 重用 Scala 现有代码的代码 List

import java.util.{ List => JList }
implicit def JListWrites[T](implicit sw: Writes[List[T]]): Writes[JList[T]]) = Write[JList[T]] { jlist => 
  sw.writes(jlist.asScala) 
}

这是一个可能的实现

import play.api.libs.json.{JsArray, JsValue, Json, Writes}
import scala.collection.JavaConverters._

implicit def jListWrites[A: Writes] = new Writes[java.util.List[A]] {
  override def writes(o: util.List[A]): JsValue = {
    JsArray(o.asScala.map(Json.toJson(_)))
  }
}

您创建的不是单个 Writes,而是可以为定义了 Writes 的任何类型创建它们的方法。

你说你想避免 JavaConversions,但正如你所看到的那样很难,因为 JsArray 无论如何都期望 Seq[JsValue] 所以你需要构建一个 scala Seq 一种或另一种方式。

我在这里展示的内容或多或少等同于使用 asScala 将 java List 转换为 scala mutable.Buffer 并为 [= 使用默认 Writes 22=].

请注意,转换可能没有您想象的那么广泛,它们只是创建一个包装器,不涉及复制。

这是我所能提供的最好的性能

implicit def jListWrites[A: Writes] = new Writes[java.util.List[A]] {
  override def writes(o: util.List[A]): JsValue = {
    val buffer = new Array[JsValue](o.size)
    var i = 0
    while (i < o.size) {
      buffer(i) = Json.toJson(o.get(i))
      i += 1
    }

    JsArray(buffer)
  }
}

1000000 Int 秒需要 29 毫秒,而直接实现需要 39 毫秒。请注意 Int 很容易转换,如果您的对象更复杂,加速比会更小。

转换其中的 20000 个 case class C(num: Int, n2: Int, s: String) 得到相同的结果(直接更快 0.14 毫秒)。