【问题标题】:Scala - Ignore case class field when decoding JSONScala - 解码 JSON 时忽略案例类字段
【发布时间】:2020-08-12 16:19:24
【问题描述】:

我正在使用 circe 文档中的示例 ADT 来重现我在 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 由两个值组成:一个简单的案例类和另一个我有专用编码器/解码器的值(在最小的例子中重现我真正遇到的问题):

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

但是,如果我尝试解码应该是包含 FoobarBar 的内容,则会出现解码失败:

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 没有办法做到这一点

【问题讨论】:

    标签: json scala circe


    【解决方案1】:

    这就是你如何使用 semi-auto 派生来实现的。

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

    使用显式解码器会有点嘈杂,但如果您关心编译速度,这是可行的方法,因为您只会派生一次解码器。

    【讨论】:

      猜你喜欢
      • 2017-09-01
      • 1970-01-01
      • 2020-05-28
      • 2016-12-08
      • 2018-05-03
      • 2018-11-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多