ADT Json serialization/deserialization
ADT Json serialization/deserialization
在 Scala 中尝试使用 Circe 序列化和反序列化代数数据类型我尝试了以下示例,遵循文档和网络上的一些示例:
sealed trait StringData
case class Data2(f1: String, f2: String) extends StringData
case class Data3(f1: String, f2: String, f3: String) extends StringData
object StringData {
// ===> does not work, always picks Data2 type
implicit val decodeData: Decoder[Data] = Decoder[OptionsData].map[Data](identity).or(Decoder[TextData].map[Data](identity))
implicit val encodeData: Encoder[StringData] = Encoder.instance {
case d2 @ Data2( _,_) => d2.asJson
case d3 @ Data3( _, _, _) => d3.asJson
}
def toJson(s: StringData): String = s.asJson.noSpaces
def fromJson(s: String): Either[Error, StringData] = decode[StringData](s)
}
"Inheritance ADT with identical fields" should "serialize and deserialize with Circe" in {
val d2 = Data2("a", "b")
val d3 = Data3("1", "2", "3")
val jd2 = StringData.toJson(d2)
val jd3 = StringData.toJson(d3)
val d2Decoded = StringData.fromJson(jd2)
val d3Decoded = StringData.fromJson(jd3)
d2Decoded.right.get should equal(d2)
d3Decoded.right.get should equal(d3)
println("")
}
问题是 d3Decoded
的类型始终是 Data2
类型,而不是所需的 Data3
类型。
我想到的解决方案是用这个替换解码器:
implicit val decodeData: Decoder[StringData] = Decoder.instance { c =>
c.downField("f3").as[String] match {
case m: Either[DecodingFailure, String] if m.isLeft => c.as[Data2]
case m: Either[DecodingFailure, String] if m.isRight => c.as[Data3]
}
}
在我看来,这是一个相当临时的解决方案。在 Jackson 中,可以将类型添加到 Json。我想知道我是否以正确的方式使用 Circe,或者这是否真的是正确的方式。非常欢迎任何评论。
尝试使用 documented here:
import io.circe.generic.extras.auto._
import io.circe.generic.extras.Configuration
import io.circe.parser.decode
implicit val genDevConfig: Configuration =
Configuration.default.withDiscriminator("what_am_i")
sealed trait StringData
case class Data2(f1: String, f2: String) extends StringData
case class Data3(f1: String, f2: String, f3: String) extends StringData
decode[StringData]("""{ "f1": "foo", "f2": "bar", "f3": "qux", "what_am_i": "Data3" }""")
输出
res0: Either[io.circe.Error,StringData] = Right(Data3(foo,bar,qux)
哪里
libraryDependencies ++= Seq(
"io.circe" %% "circe-core",
"io.circe" %% "circe-generic",
"io.circe" %% "circe-parser",
"io.circe" %% "circe-generic-extras",
).map(_ % circeVersion)
在 Scala 中尝试使用 Circe 序列化和反序列化代数数据类型我尝试了以下示例,遵循文档和网络上的一些示例:
sealed trait StringData
case class Data2(f1: String, f2: String) extends StringData
case class Data3(f1: String, f2: String, f3: String) extends StringData
object StringData {
// ===> does not work, always picks Data2 type
implicit val decodeData: Decoder[Data] = Decoder[OptionsData].map[Data](identity).or(Decoder[TextData].map[Data](identity))
implicit val encodeData: Encoder[StringData] = Encoder.instance {
case d2 @ Data2( _,_) => d2.asJson
case d3 @ Data3( _, _, _) => d3.asJson
}
def toJson(s: StringData): String = s.asJson.noSpaces
def fromJson(s: String): Either[Error, StringData] = decode[StringData](s)
}
"Inheritance ADT with identical fields" should "serialize and deserialize with Circe" in {
val d2 = Data2("a", "b")
val d3 = Data3("1", "2", "3")
val jd2 = StringData.toJson(d2)
val jd3 = StringData.toJson(d3)
val d2Decoded = StringData.fromJson(jd2)
val d3Decoded = StringData.fromJson(jd3)
d2Decoded.right.get should equal(d2)
d3Decoded.right.get should equal(d3)
println("")
}
问题是 d3Decoded
的类型始终是 Data2
类型,而不是所需的 Data3
类型。
我想到的解决方案是用这个替换解码器:
implicit val decodeData: Decoder[StringData] = Decoder.instance { c =>
c.downField("f3").as[String] match {
case m: Either[DecodingFailure, String] if m.isLeft => c.as[Data2]
case m: Either[DecodingFailure, String] if m.isRight => c.as[Data3]
}
}
在我看来,这是一个相当临时的解决方案。在 Jackson 中,可以将类型添加到 Json。我想知道我是否以正确的方式使用 Circe,或者这是否真的是正确的方式。非常欢迎任何评论。
尝试使用
import io.circe.generic.extras.auto._
import io.circe.generic.extras.Configuration
import io.circe.parser.decode
implicit val genDevConfig: Configuration =
Configuration.default.withDiscriminator("what_am_i")
sealed trait StringData
case class Data2(f1: String, f2: String) extends StringData
case class Data3(f1: String, f2: String, f3: String) extends StringData
decode[StringData]("""{ "f1": "foo", "f2": "bar", "f3": "qux", "what_am_i": "Data3" }""")
输出
res0: Either[io.circe.Error,StringData] = Right(Data3(foo,bar,qux)
哪里
libraryDependencies ++= Seq(
"io.circe" %% "circe-core",
"io.circe" %% "circe-generic",
"io.circe" %% "circe-parser",
"io.circe" %% "circe-generic-extras",
).map(_ % circeVersion)