2017 年 3 月 30 日更新:跟进 OP 的评论
我能够完成这项工作,如链接的release notes 所示。
准备步骤1:向build.sbt添加额外的依赖
libraryDependencies += "io.circe" %% "circe-generic-extras" % "0.7.0"
准备步骤 2:设置虚拟密封特征层次结构
import io.circe.{ Decoder, Encoder }
import io.circe.parser._, io.circe.syntax._
import io.circe.generic.extras.Configuration
import io.circe.generic.extras.auto._
import io.circe.generic.{ semiauto => boring } // <- This is the default generic derivation behaviour
import io.circe.generic.extras.{ semiauto => fancy } // <- This is the new generic derivation behaviour
implicit val customConfig: Configuration = Configuration.default.withDefaults.withDiscriminator("type")
sealed trait Stuff
case class Foo(thisIsAString: String, anotherField: Int = 13) extends Stuff
case class Bar(thisIsAString: String, anotherField: Int = 13) extends Stuff
object Foo {
implicit val decodeBar: Decoder[Bar] = fancy.deriveDecoder
implicit val encodeBar: Encoder[Bar] = fancy.deriveEncoder
}
object Bar {
implicit val decodeBar: Decoder[Bar] = boring.deriveDecoder
implicit val encodeBar: Encoder[Bar] = boring.deriveEncoder
}
使用这个的实际代码:
val foo: Stuff = Foo("abc", 123)
val bar: Stuff = Bar("xyz", 987)
val fooString = foo.asJson.noSpaces
// fooString: String = {"thisIsAString":"abc","anotherField":123,"type":"Foo"}
val barString = bar.asJson.noSpaces
// barString: String = {"thisIsAString":"xyz","anotherField":987,"type":"Bar"}
val bar2 = for{
json <- parse(barString)
bar2 <- json.as[Stuff]
} yield bar2
// bar2: scala.util.Either[io.circe.Error,Stuff] = Right(Bar(xyz,987))
val foo2 = for{
json <- parse(fooString)
foo2 <- json.as[Stuff]
} yield foo2
// foo2: scala.util.Either[io.circe.Error,Stuff] = Right(Foo(abc,123))
因此,只要您导入额外的依赖项(这是 Configuration 的来源),它看起来就可以工作。
最后,作为旁注,Circe's DESIGN.md 和练习之间似乎确实存在一些脱节,对此我实际上很高兴。
原答案:
我不确定这是否应该被设计支持。
取自Circe's DESIGN.md:
不应使用隐式作用域进行配置。很多人都要求提供一种方法来配置通用编解码器派生以使用例如类型字段作为密封特征层次结构的鉴别器,或使用蛇形大小写作为成员名称。 argonaut-shapeless 使用用户可以隐式提供的 JsonCoproductCodec 类型非常直接地支持这一点。
我不想批评这种方法——它完全是惯用的 Scala,而且在实践中通常效果很好——但我个人不喜欢使用隐式值进行配置,我想避免它直到我 100% 确信没有其他方法可以提供此功能。
这具体意味着什么:您可能永远不会看到不是类型类实例的隐式参数 - 即这不是应用于模型中的类型的类型构造函数——大约,通用编解码器派生的配置将相对有限(与例如 argonaut-shapeless 相比),直到我们找到一种很好的方法来做这种事情带有类型标签或类似的东西。
特别是,customConfig: Configuration 似乎正是最后一段引用的参数类型(例如,一个不是类型类实例的隐式参数)
我相信@travis-brown 或任何其他 Circe 的主要贡献者可以对此有所了解,以防实际上有办法做到这一点 - 我很高兴知道它! :)