为什么它不解码为 ADT 类型?
Why it does not decode to ADT type?
我正在尝试将以下字符串导出为正确的 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
不正确。
我不太确定 MsgDocDerivation
的目的是什么——这似乎是不必要的,而且会让人分心——但我认为关键问题是 circe 的编码(和解码)是由静态类型驱动的,不是正在编码(或解码)的值的运行时 class。这意味着以下两个 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))
这是因为我执行了从 MsgProceed
到 MsgDoc
的(类型安全的)向上转换。无论如何,这通常是您使用 ADT 的方式——您不是传递静态类型的值作为 case class 子类型,而是作为 sealed trait
基本类型。
问题在于 MsgProceed
类型 MsgProceed
和类型 MsgDoc
被编码为不同的 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()))
我正在尝试将以下字符串导出为正确的 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
不正确。
我不太确定 MsgDocDerivation
的目的是什么——这似乎是不必要的,而且会让人分心——但我认为关键问题是 circe 的编码(和解码)是由静态类型驱动的,不是正在编码(或解码)的值的运行时 class。这意味着以下两个 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))
这是因为我执行了从 MsgProceed
到 MsgDoc
的(类型安全的)向上转换。无论如何,这通常是您使用 ADT 的方式——您不是传递静态类型的值作为 case class 子类型,而是作为 sealed trait
基本类型。
问题在于 MsgProceed
类型 MsgProceed
和类型 MsgDoc
被编码为不同的 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()))