在 Scala (Play Framework) 中将对象序列化为 JSON 原始类型

Serialize objects as JSON primitive type in Scala (Play Framework)

查看以下示例:

object MyTrait {
  def apply(value: Int): MyTrait = value match {
    case 0 => A
    case 1 => B
    case 2 => C
    case _ => C
  }

  def unapply(arg: MyTrait): Int = arg.value
}

case object A extends MyTrait {
  val value = 0
}

case object B extends MyTrait {
  val value = 1
}

case object C extends MyTrait {
  val value = 2
}

case class Test(value: MyTrait)

implicit val testFormatter = Json.format[Test]

好吧,这段代码无法运行,因为我没有为我的类型“MyTrait”创建 JSON 序列化程序。我可以按照 Play Framework 在文档中向我们展示的方式来做,但它会使 json 看起来像

{ value: { whatever: 1 } }

我希望它看起来像

{ value: 1 }

简而言之,我希望我的 MyTrait 对象被解释为原始类型 (Int) 而不是嵌套的 Json 对象。

如果有人能帮助我,我将不胜感激。 提前致谢!

documentation 指示如何提供自定义 WritesMyTrait 仅序列化它的内部 value

sealed trait MyTrait {
  def value: Int
}

case object A extends MyTrait {
  val value = 0
}

case object B extends MyTrait {
  val value = 1
}

case object C extends MyTrait {
  val value = 2
}

object MyTrait {
  import play.api.libs.json._

  def apply(value: Int): MyTrait = value match {
    case 0 => A
    case 1 => B
    case 2 => C
    case _ => C
  }

  def unapply(arg: MyTrait): Int = arg.value

  // ---v
  implicit val writes: Writes[MyTrait] =
    Writes[MyTrait] { v => JsNumber(v.value) }
}

然后在序列化 MyTrait 实例时(注意下面的类型归属是必需的,因为 Writes 是不变的):

Json.toJson(A: MyTrait)
// --> res2: play.api.libs.json.JsValue = 0

等等 Test class:

case class Test(value: MyTrait)

object Test {
  import play.api.libs.json._

  implicit val writes: OWrites[Test] = Json.writes[Test]
}

Json.toJson(Test(A))
// ---> res1: play.api.libs.json.JsValue = {"value":0}

我宁愿推荐看看 Enumeratum for the enumerated type MyTrait, or to refactor MyTrait as a Value class 并使用 Json.valueFormat 如下。

import play.api.libs.json._

final class MyTrait private(val value: Int) extends AnyVal

object MyTrait {
  val A = new MyTrait(0)
  val B = new MyTrait(1)
  val C = new MyTrait(2)

  implicit val format: Format[MyTrait] = Json.valueFormat[MyTrait]
}

case class Test(value: MyTrait)

object Test {
  implicit val format: OFormat[Test] = Json.format
}

scala> Json.toJson(Test(MyTrait.A))
res0: play.api.libs.json.JsValue = {"value":0}