Play 2.1中的JSON序列化是否支持traits?

10

我有这个:

package models

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

object ModelWrites {
    implicit val tmoWrites= Json.writes[TestModelObject]
    implicit val ihWrites = Json.writes[IntHolder]
}

case class TestModelObject(s1:String, s2:String)

case class IntHolder(i1:Int, i2:Int)

trait HasInts {
    val ints: List[IntHolder]
}

当我这样做时:

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

scala> Json.toJson(tmo)
res0: play.api.libs.json.JsValue = {"s1":"hello","s2":"world"}

我如何隐式地序列化混入的变量'ints'?例如:

scala> val someInts = List(IntHolder(8,9), IntHolder(10,11))
someInts: List[models.IntHolder] = List(IntHolder(8,9), IntHolder(10,11))

scala> Json.toJson(someInts)
res1: play.api.libs.json.JsValue = [{"i1":8,"i2":9},{"i1":10,"i2":11}]
注意:如果我尝试使用implicit val hasIntsWrites = Json.writes[HasInts],我是否(预期地?)会得到以下结果:
[error] Models.scala:10: No unapply function found
[error]     implicit val hasIntsWrites = Json.writes[HasInts]
[error]                                             ^
1个回答

9

由于Json.writes[...]只适用于case类,因此您无法直接使用实验性的"Inception"功能。但是,您可以基于Inception提供的Writes实例来完成您想要做的事情,只需要很少的样板文件。

请注意,我忽略了在实例化这样的case类时混入trait是否是一个好主意的问题 - 它可能不是 - 但是我在这里给出的方法也适用于更一般的情况。

首先是类和导入(此处没有更改):

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._

现在我们需要将所有低优先级的实例放入一个trait中,以确保编译器选择正确的实例,因为 TestModelObject with HasIntsTestModelObjectHasInts 的子类型:

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)
  }
}

现在是主要内容:

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)
  }
}

我们完成了:

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}]}

如所期望的。

太棒了,正是我在寻找的。谢谢!“请注意,我忽略了在实例化这样的 case class 时混入 trait 是否是一个好主意的问题——它可能不是。”如果您愿意解释一下,我全耳倾听 :) - Lester Burnham
现在我开始对自己产生了怀疑。“逐案”继承长期以来一直被反对,并且在2.10中被禁止,但是你在这里所做的(匿名继承一个case类)可能不会引起任何相同的问题。因此,您可以忽略那个方面。我会尽量在有几分钟进行更详细检查时更新答案。 - Travis Brown

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接