1

次の例を考えます。

sealed trait Id

case class NewId(prefix: String, id: String) extends Id
case class RevisedId(prefix: String, id: String, rev: String) extends Id

case class User(key: Id, name: String)

val json = """
{
  "key": {
    "prefix": "user",
    "id": "Rt01",
    "rev": "0-1"
  },
  "name": "Bob Boberson"
}
"""

implicit val CodecUser: CodecJson[User] = casecodec2(User.apply, User.unapply)("key", "name")

implicit val CodecId: CodecJson[Id] = ???

json.decodeOption[User]

適切な構造を持つオブジェクトをデコードするCodecJsonforを記述する必要があります。Id

spray-json何らかの種類の識別子フィールドを追加することは、これに対する一般的な提案ですが、既に生成/消費している JSON を変更したくありませんjson4s

これらのライブラリでは、エンコーダー/デコーダーは基本的にPartialFunction[JValue, A]PartialFunction[A, JValue]. 値がドメインで定義されていない場合、それは失敗です。これは本当にシンプルでエレガントなソリューションだと思います。それに加えて、JSON タイプのエクストラクタがあるため、フィールド/構造でオブジェクトを簡単に一致させることができます。

Rapture はさらに一歩進んで、フィールドの順序を重要ではなく、一致しないフィールドの存在を無視するので、次のようにすることができます。

case json"""{ "prefix": $prefix, "id": $id, "rev": $rev }""" => 
  RevisedId(prefix, id, rev)

それは本当にシンプル/強力です。

で同様のことを行う方法がわかりませんargonaut。これは私がこれまでに思いついた最高のものです:

val CodecNewId = casecodec2(NewId.apply, NewId.unapply)("prefix", "id")
val CodecRevisedId = casecodec3(RevisedId.apply, RevisedId.unapply)("prefix", "id", "rev")

implicit val CodecId: CodecJson[Id] =
  CodecJson.derived[Id](
    EncodeJson {
      case id: NewId => CodecNewId(id)
      case id: IdWithRev => RevisedId(id)
    },
    DecodeJson[Id](c => {
      val q = RevisedId(c).map(a => a: Id)
      q.result.fold(_ => CodecNewId(c).map(a => a: Id), _ => q)
    })
  )

そのため、いくつかの問題があります。使用する予定のない追加のコーデックを定義する必要があります。EncodeJsonforで case-class エクストラクタを使用する代わりに、CodecJson[Id]定義した他のエンコーダに委任しています。ただ、フィールドが 2 つまたは 3 つしかないクラスでは、あまり単純でもきれいでもありません。

このセクションのコードDecodeJsonもかなり厄介です。ifEmptyの側に追加の型キャストがあることを除けばfold、 のコードと同じですDecodeJson.|||

誰かが、ディスクリミネーターを必要とせず、代わりにjsonの構造に一致できるアルゴナウトでSumタイプの基本的なコーデックを書くためのより慣用的な方法を持っていますか?

4

1 に答える 1