writes play framework json scala playframework

framework - Formato JSON sin ruido para rasgos sellados con la biblioteca Play 2.2



play json github (3)

Aquí hay una implementación manual del objeto complementario 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!

Verificación:

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)

Alternativamente, la definición de formato directo:

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

Ahora, idealmente, me gustaría generar automáticamente los métodos de apply y unapply . Parece que necesitaré usar reflexión o inmersión en macros.

Necesito obtener una solución de serialización JSON simple con la ceremonia mínima. Así que me alegré mucho al encontrar esta próxima biblioteca de Play 2.2 . Esto funciona perfectamente con clases de casos simples, por ejemplo

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]

Pero el siguiente falla:

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

¿Cómo instalaría el presunto extractor extraído para Foo ?

¿O recomendaría alguna otra biblioteca independiente que maneje mi caso de forma más o menos completamente automática? No me importa si eso es con macros en tiempo de compilación o reflexión en tiempo de ejecución, siempre y cuando salga de la caja.


Una pequeña solución para la respuesta anterior en 0__ con respecto a la definición de formato directo - el método de lectura no funcionó, y aquí está mi refactorio, para volverse más idiomático -

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 }


ENMENDADO 2015-09-22

La biblioteca play-json-extra incluye la estrategia play-json-variants , pero también la estrategia [play-json-extensions] (cadena plana para objetos de mayúsculas y minúsculas mezclados con objetos para clases de casos sin variante $ extra o $ type a menos que sea necesario). También proporciona serializadores y deserializadores para enums basados ​​en macramé .

Respuesta anterior Ahora hay una biblioteca llamada play-json-variants que te permite escribir:

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

Esto generará automáticamente los formatos correspondientes, también manejará la desambiguación del caso siguiente al agregar un atributo $ variant (el equivalente del atributo de class de 0__)

sealed trait Foo case class Bar(x: Int) extends Foo case class Baz(s: String) extends Foo case class Bah(s: String) extends Foo

generaría

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`