我需要以最少的仪式获得一个简单的JSON序列化解决方案。所以我很高兴找到this forthcoming Play 2.2 library。这适用于普通的案例类,例如
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
设置所谓的缺失提取器?
或者你会推荐任何其他独立的库,或多或少全自动处理我的案例?我不关心是在编译时使用宏还是在运行时使用反射,只要它开箱即用。
答案 0 :(得分:23)
以下是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!
验证
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))
}
}
理想情况下,我想自动生成apply
和unapply
方法。我似乎需要使用反射或潜入宏。
答案 1 :(得分:22)
AMENDED 2015-09-22
库play-json-extra包括play-json-variants策略,还有[play-json-extensions]策略(用于案例对象的案例对象的扁平字符串,没有额外的$ 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`
答案 2 :(得分:3)
关于直接格式定义的前一个答案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
}
答案 3 :(得分:2)
播放2.6 / 2.7
现在可以使用play-json-derived-codecs
轻松地完成此操作只需添加:
object Foo{
implicit val jsonFormat: OFormat[Foo] = derived.oformat[Foo]()
}
有关完整示例,请参见此处:ScalaFiddle