使用Play 2.2库实现密封特性的无噪声JSON格式

0__*_*0__ 44 json scala playframework

我需要以最少的仪式获得一个简单的JSON序列化解决方案.所以我很高兴找到即将推出的Play 2.2库.这与普通案例类完美匹配,例如

import play.api.libs.json._

sealed trait Foo
case class Bar(i: Int) extends Foo
case class Baz(f: Float) extends Foo

implicit val barFmt = Json.format[Bar]
implicit val bazFmt = Json.format[Baz]
Run Code Online (Sandbox Code Playgroud)

但是以下失败了:

implicit val fooFmt = Json.format[Foo]   // "No unapply function found"
Run Code Online (Sandbox Code Playgroud)

我如何设置所谓的缺失提取器Foo

或者你会推荐任何其他独立的库,或多或少全自动处理我的情况?我不关心是在编译时使用宏还是在运行时使用反射,只要它开箱即用即可.

0__*_*0__ 24

以下是Foo配套对象的手动实现:

implicit val barFmt = Json.format[Bar]
implicit val bazFmt = Json.format[Baz]

object Foo {
  def unapply(foo: Foo): Option[(String, JsValue)] = {
    val (prod: Product, sub) = foo match {
      case b: Bar => (b, Json.toJson(b)(barFmt))
      case b: Baz => (b, Json.toJson(b)(bazFmt))
    }
    Some(prod.productPrefix -> sub)
  }

  def apply(`class`: String, data: JsValue): Foo = {
    (`class` match {
      case "Bar" => Json.fromJson[Bar](data)(barFmt)
      case "Baz" => Json.fromJson[Baz](data)(bazFmt)
    }).get
  }
}
sealed trait Foo
case class Bar(i: Int  ) extends Foo
case class Baz(f: Float) extends Foo

implicit val fooFmt = Json.format[Foo]   // ça marche!
Run Code Online (Sandbox Code Playgroud)

验证:

val in: Foo = Bar(33)
val js  = Json.toJson(in)
println(Json.prettyPrint(js))

val out = Json.fromJson[Foo](js).getOrElse(sys.error("Oh no!"))
assert(in == out)
Run Code Online (Sandbox Code Playgroud)

或者直接格式定义:

implicit val fooFmt: Format[Foo] = new Format[Foo] {
  def reads(json: JsValue): JsResult[Foo] = json match {
    case JsObject(Seq(("class", JsString(name)), ("data", data))) =>
      name match {
        case "Bar"  => Json.fromJson[Bar](data)(barFmt)
        case "Baz"  => Json.fromJson[Baz](data)(bazFmt)
        case _      => JsError(s"Unknown class '$name'")
      }

    case _ => JsError(s"Unexpected JSON value $json")
  }

  def writes(foo: Foo): JsValue = {
    val (prod: Product, sub) = foo match {
      case b: Bar => (b, Json.toJson(b)(barFmt))
      case b: Baz => (b, Json.toJson(b)(bazFmt))
    }
    JsObject(Seq("class" -> JsString(prod.productPrefix), "data" -> sub))
  }
}
Run Code Online (Sandbox Code Playgroud)

现在理想情况下我想自动生成applyunapply方法.我似乎需要使用反射或潜入宏.


Jea*_*ean 24

修订2015-09-22

play-json-extra包括play-json-variants策略,还有[play-json-extensions]策略(用于案例类的案例对象的扁平字符串,除非需要,否则没有额外的$ variant或$ type).它还提供了串行器和解串器 花边基于枚举.

上一个答案 现在有一个名为play-json-variants的库,它允许你编写:

implicit val format: Format[Foo] = Variants.format[Foo]
Run Code Online (Sandbox Code Playgroud)

这将自动生成相应的格式,它还将通过添加$ variant属性(相当于0__的class属性)来处理以下情况的歧义消除

sealed trait Foo
case class Bar(x: Int) extends Foo
case class Baz(s: String) extends Foo
case class Bah(s: String) extends Foo
Run Code Online (Sandbox Code Playgroud)

会产生

val bahJson = Json.obj("s" -> "hello", "$variant" -> "Bah") // This is a `Bah`
val bazJson = Json.obj("s" -> "bye", "$variant" -> "Baz") // This is a `Baz`
val barJson = Json.obj("x" -> "42", "$variant" -> "Bar") // And this is a `Bar`
Run Code Online (Sandbox Code Playgroud)


pme*_*pme 7

玩2.7

sealed traits受支持play-json

object Foo{
  implicit val format = Json.format[Foo]
}
Run Code Online (Sandbox Code Playgroud)

玩2.6

现在可以使用play-json-driven-codecs优雅地完成此操作

只需添加以下内容:

object Foo{
    implicit val jsonFormat: OFormat[Foo] = derived.oformat[Foo]()
}
Run Code Online (Sandbox Code Playgroud)

完整示例请参见此处:ScalaFiddle