Scala - игнорировать поле класса дела при декодировании JSON - PullRequest
1 голос
/ 28 апреля 2020

Я играю с примером ADT в документации Circe, чтобы воспроизвести проблему, которая у меня возникла с декодированием JSON.

Для этого я использую ShapesDerivation:

scala>   object ShapesDerivation {
     | 
     |     implicit def encodeAdtNoDiscr[Event, Repr <: Coproduct](implicit
     |       gen: Generic.Aux[Event, Repr],
     |       encodeRepr: Encoder[Repr]
     |     ): Encoder[Event] = encodeRepr.contramap(gen.to)
     | 
     |     implicit def decodeAdtNoDiscr[Event, Repr <: Coproduct](implicit
     |       gen: Generic.Aux[Event, Repr],
     |       decodeRepr: Decoder[Repr]
     |     ): Decoder[Event] = decodeRepr.map(gen.from)
     | 
     |   }
defined object ShapesDerivation

ADT для декодирования состоит из двух значений: простого класса case и другого, который я выделил Encoder / Decoder (чтобы воспроизвести в минимальном примере проблему, которая у меня действительно есть):

scala> :paste
// Entering paste mode (ctrl-D to finish)

 sealed trait Event

  object Event {

    case class Foo(i: Int) extends Event

    case class Bar(f : FooBar) extends Event

    case class FooBar(x : Int) 

    implicit val encoderFooBar : Encoder[FooBar] = new Encoder[FooBar] {
      override def apply(a: FooBar): Json = Json.obj(("x", Json.fromInt(a.x)))
    }

    implicit val decodeFooBar: Decoder[FooBar] = new Decoder[FooBar] {
      override def apply(c: HCursor): Result[FooBar] =
        for {
          x <- c.downField("x").as[Int]
        } yield FooBar(x)
    }
  }

Затем, когда я пытаюсь декодировать простое значение, подобное этому, оно работает хорошо:

scala> import ShapesDerivation._
import ShapesDerivation._

scala> decode[Event](""" { "i" : 10 }""")
res1: Either[io.circe.Error,Event] = Right(Foo(10))

Но если я попытался декодировать что-то, что должно быть Bar, которое содержит Foobar, я получаю декодирование ошибка:

scala> decode[Event](""" { "x" : 10 }""")
res2: Either[io.circe.Error,Event] = Left(DecodingFailure(CNil, List()))

Но это работает, потому что я явно поставил имя поля класса дела:

scala> decode[Event](""" { "f" : { "x" : 10 }}""")
res7: Either[io.circe.Error,Event] = Right(Bar(FooBar(10)))

Я не знаю, как поместить поле класса дела, непосредственно JSON но я думаю, что невозможно добиться такого поведения. Причина, по которой я думаю, что это невозможно, состоит в том, как он узнает, что он соответствует классу хорошего случая, если нет поля, но я хочу быть уверен, что с помощью Circe нет способа сделать это

1 Ответ

2 голосов
/ 28 апреля 2020

Вот как вы это делаете, используя полуавтоматическое деривация.

import io.circe.Decoder.Result
import io.circe.{Decoder, Encoder, HCursor, Json}
import io.circe.parser._
import io.circe.generic.semiauto._

object Example extends App {

  sealed trait Event

  object Event {

    case class Foo(i: Int) extends Event

    object Foo {
      implicit val decoder: Decoder[Foo] = deriveDecoder
    }

    case class Bar(f: FooBar) extends Event

    object Bar {
      implicit val decoder: Decoder[Bar] = Decoder[FooBar].map(Bar.apply)
    }

    implicit val decoder: Decoder[Event] = Decoder[Foo].widen.or(Decoder[Bar].widen)
  }

  case class FooBar(x: Int)

  object FooBar {
    implicit val encoderFooBar: Encoder[FooBar] = deriveEncoder
    implicit val decodeFooBar: Decoder[FooBar]  = deriveDecoder
  }

  println(decode[Event](""" { "x" : 10 }"""))
}

Выходы

Right(Bar(FooBar(10)))

Это становится немного шумно с явными декодерами, но если вы заботитесь о скорости компиляции, это путь к go, поскольку вы будете получать декодеры только один раз.

...