Написание DecodeJson для запечатанной иерархии признаков?

Данный:

import argonaut._
import Argonaut._
import ArgonautShapeless._

sealed trait Parent
case class Foo(x: Int)    extends Parent
case class Bar(y: String) extends Parent

Я попытался определить DecodeJson[Parent]:

implicit val parentDecodeJson: DecodeJson[Parent] = 
   DecodeJson(c => c.focus.objectFields match {
       case Some("x" :: _) => implicitly[DecodeJson[Foo]].decode(c)
      case _                => implicitly[DecodeJson[Bar]].decode(c)
   })

Однако это не удалось, поскольку argonaut.DecodeResult является инвариантным.

<console>:42: error: type mismatch;
 found   : argonaut.DecodeResult[Foo]
 required: argonaut.DecodeResult[Parent]
Note: Foo <: Parent, but class DecodeResult is invariant in type A.
You may wish to define A as +A instead. (SLS 4.5)
              case Some("x" :: _) => implicitly[DecodeJson[Foo]].decode(c)
                                                                       ^
<console>:43: error: type mismatch;
 found   : argonaut.DecodeResult[Bar]
 required: argonaut.DecodeResult[Parent]
Note: Bar <: Parent, but class DecodeResult is invariant in type A.
You may wish to define A as +A instead. (SLS 4.5)
             case _     => implicitly[DecodeJson[Bar]].decode(c)
                                                             ^

Итак, я придумал:

implicit val parentDecodeJson: DecodeJson[Parent] = 
   DecodeJson(c => c.focus.objectFields match {
       case Some("x" :: _) => implicitly[DecodeJson[Foo]].decode(c).flatMap{a => DecodeResult.ok(a)}
      case _     => implicitly[DecodeJson[Bar]].decode(c).flatMap{a => DecodeResult.ok(a)}
   })

Кажется, это работает:

scala> Json.obj( ("x", jNumber(42)) ).as[Parent]
res2: argonaut.DecodeResult[Parent] = DecodeResult(Right(Foo(42)))

scala> Json.obj( ("y", jString("hi!")) ).as[Parent]
res3: argonaut.DecodeResult[Parent] = DecodeResult(Right(Bar(hi!)))

Есть ли более чистый способ?


person Kevin Meredith    schedule 17.10.2017    source источник
comment
Это должно работать из коробки, по крайней мере, из того, что говорится в README (github.com/alexarchambault/argonaut- бесформенный)   -  person Yuval Itzchakov    schedule 18.10.2017
comment
как насчет этого: stackoverflow.com/questions/39108841/   -  person tkachuko    schedule 18.10.2017


Ответы (1)


Вам не нужно определять свои собственные имплициты:

println(EncodeJson.of[Parent].encode(Bar("a")))//{"Bar":{"y":"a"}}
println(EncodeJson.of[Parent].apply(Bar("a")))//{"Bar":{"y":"a"}}
println(Bar("a").asJson)//{"y":"a"}
println((Bar("a"): Parent).asJson)//{"Bar":{"y":"a"}}
println("{\"Bar\":{\"y\":\"a\"}}".decode[Parent])//Right(Bar(a))
println("{\"Bar\":{\"y\":\"a\"}}".decodeOption[Parent])//Some(Bar(a))

Или я неправильно понимаю вашу цель?

person Dmytro Mitin    schedule 18.10.2017