【问题标题】:Why it does not decode to ADT type?为什么它不解码为 ADT 类型?
【发布时间】:2019-09-23 10:15:11
【问题描述】:

我正在尝试将以下字符串派生为正确的 ADT 类型:

res6: String = {"raw":"Hello","status":{"MsgSuccess":{}}} 

并使用circe 库。

ADT 类型如下所示:

sealed trait MsgDoc {
}

final case class MsgPreFailure(raw: String, reasons: Chain[String]) extends MsgDoc

final case class MsgProceed(raw: String, status: MsgStatus) extends MsgDoc

MsgStatus 类型:

sealed trait MsgStatus {

}

case object MsgSuccess extends MsgStatus

final case class MsgFailure(reasons: Chain[String]) extends MsgStatus

final case class MsgUnknown(reason: String) extends MsgStatus

顺便说一句,我试过开车:

object MsgDocDerivation {

  import shapeless.{Coproduct, Generic}

  implicit def encodeAdtNoDiscr[A, Repr <: Coproduct](implicit
                                                      gen: Generic.Aux[A, Repr],
                                                      encodeRepr: Encoder[Repr]
                                                     ): Encoder[A] = encodeRepr.contramap(gen.to)

  implicit def decodeAdtNoDiscr[A, Repr <: Coproduct](implicit
                                                      gen: Generic.Aux[A, Repr],
                                                      decodeRepr: Decoder[Repr]
                                                     ): Decoder[A] = decodeRepr.map(gen.from)
}

和执行:

object Main extends App {


  val json = MsgProceed("Hello", MsgSuccess).asJson
  println(json)
  val adt = decode[MsgDoc](json.noSpaces)
  println(adt)

} 

我得到的结果是:

{
  "raw" : "Hello",
  "status" : {
    "MsgSuccess" : {

    }
  }
}
Left(DecodingFailure(CNil, List())) 

如您所见,它不能正确地decode

源码可以在https://gitlab.com/playscala/adtjson找到。

【问题讨论】:

    标签: scala circe


    【解决方案1】:

    我不太确定 MsgDocDerivation 的用途是什么——这似乎是不必要且令人分心的——但我认为关键问题是 circe 的编码(和解码)是由静态类型驱动的,而不是运行时类被编码(或解码)的值。这意味着以下两个 JSON 值会有所不同:

    val value = MsgProceed("Hello", MsgSuccess)
    
    val json1 = value.asJson
    val json2 = (value: MsgDoc).asJson
    

    在你的情况下,以下对我来说很好:

    import cats.data.Chain
    
    sealed trait MsgStatus
    case object MsgSuccess extends MsgStatus
    final case class MsgFailure(reasons: Chain[String]) extends MsgStatus
    final case class MsgUnknown(reason: String) extends MsgStatus
    
    sealed trait MsgDoc
    final case class MsgPreFailure(raw: String, reasons: Chain[String]) extends MsgDoc
    final case class MsgProceed(raw: String, status: MsgStatus) extends MsgDoc
    
    import io.circe.generic.auto._, io.circe.jawn.decode, io.circe.syntax._
    
    val value: MsgDoc = MsgProceed("Hello", MsgSuccess)
    val json = value.asJson
    
    val backToValue = decode[MsgDoc](json.noSpaces)
    

    请注意,json 与您看到的不同:

    scala> json
    res0: io.circe.Json =
    {
      "MsgProceed" : {
        "raw" : "Hello",
        "status" : {
          "MsgSuccess" : {
    
          }
        }
      }
    }
    
    scala> backToValue
    res1: Either[io.circe.Error,MsgDoc] = Right(MsgProceed(Hello,MsgSuccess))
    

    这是因为我执行了从MsgProceedMsgDoc 的(类型安全的)向上转换。无论如何,这通常是您使用 ADT 的方式——您不会传递静态类型为案例类子类型的值,而是作为 sealed trait 基类型。

    【讨论】:

    • 泛型派生的目的是什么?
    • @zero_coding 你能解释一下这个问题吗?泛型派生可让您避免写出类型类实例,而无需使用运行时反射或以其他方式放弃类型安全。
    • 啊哈...非常感谢@Travis。太棒了。
    • @zero_coding 这里 Circe 为您进行通用推导,因此您不必通过 Shapeless 手动进行。
    【解决方案2】:

    问题是MsgProceed 类型为MsgProceedMsgDoc 类型被编码为不同的json。所以你尝试从错误的json解码MsgDoc

      val json = MsgProceed("Hello", MsgSuccess).asJson
      println(json)
      //{
      //  "raw" : "Hello",
      //  "status" : {
      //    "MsgSuccess" : {
      //
      //    }
      //  }
      //}
      val json1 = (MsgProceed("Hello", MsgSuccess): MsgDoc).asJson
      println(json1)
      //{
      //  "MsgProceed" : {
      //    "raw" : "Hello",
      //    "status" : {
      //      "MsgSuccess" : {
      //
      //      }
      //    }
      //  }
      //}
      val adt0 = decode[MsgProceed](json.noSpaces)
      println(adt0)
      //Right(MsgProceed(Hello,MsgSuccess))
      val adt1 = decode[MsgDoc](json1.noSpaces)
      println(adt1)
      //Right(MsgProceed(Hello,MsgSuccess))
      val adt = decode[MsgDoc](json.noSpaces)
      println(adt)
      //Left(DecodingFailure(CNil, List()))
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-04-19
      • 1970-01-01
      • 1970-01-01
      • 2019-10-24
      • 2015-07-01
      • 1970-01-01
      • 2018-11-02
      • 1970-01-01
      相关资源
      最近更新 更多