Reputation: 2002
I am working on an backend server in Play Framework in Scala. However, I am calling an external library (written in java) that returns a Java list (util.List). I created a writes for the object that is contained in the List, however I don't know how to write the writes for the actual List so that it can be generic (no need to write a "writes" for both List and List, just the writes for A and B).
I know I could use JavaConversions to convert the Java list to a Scala Seq (that already has Writes implemented), but since speed is essential, I would like to not do the extra conversion.
Upvotes: 1
Views: 466
Reputation: 8663
Here is a possible implementation
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(_)))
}
}
You don't create a single Writes
but rather a method that can create them for any type that has Writes
defined.
You said you want to avoid JavaConversions
, but as you can see it is difficult as JsArray
expects a Seq[JsValue]
anyway so you need to construct a scala Seq
one way or another.
What I shown here is more or less equivalent to converting java List
to scala mutable.Buffer
using asScala
and using default Writes
for Traversable
.
Note that conversions are probably not as expansive as you think, they just create a wrapper, no copying involved.
Here is the best what I could come with in terms of performance
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)
}
}
It takes 29 ms for 1000000 Int
s compared to 39 ms for the straightforward implementation. Note that Int
is easy to convert, if your objects are more complex the speedup will be smaller.
Converting 20000 of those case class C(num: Int, n2: Int, s: String)
gives equal results (straightforward is even faster by 0.14 ms).
Upvotes: 2
Reputation: 9168
You can code a Writes
that reuses the existing one for 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)
}
Upvotes: 1