Play 2.1 Json serialization for traits?

荒凉一梦 提交于 2019-11-30 05:10:24

You're not going to be able to use the experimental "Inception" feature (Json.writes[...]) directly here, since that only works on case classes. You can, however, build on the Writes instances that Inception can provide to accomplish what you want with only a very little boilerplate.

Note that I'm ignoring the question of whether mixing in a trait when instantiating a case class like this is a good idea—it probably isn't—but the approach I give here will work in the more general case as well.

First for the classes and imports (no changes here):

case class TestModelObject(s1: String, s2: String)
case class IntHolder(i1: Int, i2: Int)
trait HasInts { val ints: List[IntHolder] }

import play.api.libs.json._
import play.api.libs.functional.syntax._

Now we need to put all our lower-priority instances into a trait to make sure that the compiler will pick the right one, since TestModelObject with HasInts is a subtype of both TestModelObject and HasInts:

trait LowPriorityWritesInstances {
  implicit val tmoWrites = Json.writes[TestModelObject]
  implicit val ihWrites = Json.writes[IntHolder]

  implicit object hiWrites extends OWrites[HasInts] {
    def writes(hi: HasInts) = Json.obj("ints" -> hi.ints)
  }
}

And now the main event:

object WritesInstances extends LowPriorityWritesInstances {
  implicit val tmowhiWrites = new Writes[TestModelObject with HasInts] {
    def writes(o: TestModelObject with HasInts) =
      tmoWrites.writes(o) ++ implicitly[OWrites[HasInts]].writes(o)
  }
}

And we're done:

scala> import WritesInstances._
import WritesInstances._

scala> val tmo = new TestModelObject("hello", "world") with HasInts {
     |   val ints = List(IntHolder(1, 2), IntHolder(3, 4))
     | }

scala> println(Json.toJson(tmo))
{"s1":"hello","s2":"world","ints":[{"i1":1,"i2":2},{"i1":3,"i2":4}]}

As desired.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!