用于Play 2.2库的无噪声JSON格式,用于密封特征

我需要用最less的仪式来获得一个简单的JSON序列化解决scheme。 所以我很高兴find即将推出的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] 

但是下面的失败:

 implicit val fooFmt = Json.format[Foo] // "No unapply function found" 

我将如何为Foo设置所谓的失踪提取器?

或者,你会推荐任何其他独立的库,或多或less地自动处理我的情况? 我不在乎是在编译时使用macros,还是在运行时reflection,只要它是开箱即用的。

以下是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! 

validation:

 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) 

或者直接格式定义:

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

现在理想情况下,我想自动生成applyunapply方法。 看来我将需要使用reflection或潜入macros。

修改date:2015-09-22

图书馆play-json-extra包括play-json-variants策略,也包括[play-json-extensions]策略(案例对象混合的case字段的扁平string,除非需要,否则不需要额外的$ variant或$ type)。 它还为基于macramé的枚举提供序列化器和反序列化器。

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

 implicit val format: Format[Foo] = Variants.format[Foo] 

这将自动生成相应的格式,它还将通过添加$ 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 

会产生

 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` 

0__关于直接格式定义的一个小修复 – 读取方法不起作用,这里是我的重构,也变得更习惯 –

 def reads(json: JsValue): JsResult[Foo] = { def from(name: String, data: JsObject): JsResult[Foo] = name match { case "Bar" => Json.fromJson[Bar](data)(barFmt) case "Baz" => Json.fromJson[Baz](data)(bazFmt) case _ => JsError(s"Unknown class '$name'") } for { name <- (json \ "class").validate[String] data <- (json \ "data").validate[JsObject] result <- from(name, data) } yield result }