48

最小限のセレモニーで単純な 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]

しかし、以下は失敗します:

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

の不足していると言われているエクストラクターをどのようにセットアップしFooますか?

または、私のケースを多かれ少なかれ完全に自動的に処理する他のスタンドアロン ライブラリをお勧めしますか? そのままで動作する限り、それがコンパイル時のマクロであろうと、実行時のリフレクションであろうと、私は気にしません。

4

4 に答える 4

26

2015 年 9 月 22 日修正

ライブラリplay-json-extraにはplay-json-variants戦略が含まれていますが、[play-json-extensions] 戦略も含まれています (必要な場合を除き、ケース クラスのオブジェクトと混合されたケース オブジェクトのフラット文字列。余分な $variant または $type はありません)。また、 マクラメベースの列挙型のシリアライザーとデシリアライザーも提供します。

前の回答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`
于 2013-12-16T16:57:31.750 に答える
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!

検証:

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ます。リフレクションを使用するか、マクロに飛び込む必要があるようです。

于 2013-06-17T10:28:46.917 に答える