2

当使用 circe 编码为 Json 时,我们真的希望该type字段显示例如

scala> val fooJson = foo.asJson
fooJson: io.circe.Json =
{
  "this_is_a_string" : "abc",
  "another_field" : 123,
  "type" : "Foo"
}

这取自先前提到您可以像这样配置编码的发行说明:

implicit val customConfig: Configuration = 
Configuration.default.withSnakeCaseKeys.withDefaults.withDiscriminator("type")

此外,关于 circe here的其他信息表明,在没有任何配置的情况下,您应该在编码 json 中获取一些类类型信息。

我错过了什么吗?你如何让类类型显示?

4

1 回答 1

7

更新 30/03/2017:跟进 OP 的评论

如链接的发行说明中所示,我能够完成这项工作。

准备步骤 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 的 DESIGN.md与实践之间似乎存在一些脱节,对此我感到很高兴。


原始答案: 我不确定这是否应该被设计支持。

取自Circe 的 DESIGN.md

不应使用隐式范围进行配置。很多人都要求提供一种配置通用编解码器派生的方法,以使用例如类型字段作为密封特征层次结构的鉴别器,或者使用蛇形大小写作为成员名称。argonaut-shapeless 使用用户可以隐式提供的 JsonCoproductCodec 类型非常直接地支持这一点。

我不想批评这种方法——它完全是惯用的 Scala,而且在实践中通常效果很好——但我个人不喜欢使用隐式值进行配置,我想在 100 岁之前避免使用它% 确信没有其他方法可以提供此功能。

这意味着什么相对有限(与例如 argonaut-shapeless 相比),直到我们找到一种使用类型标签或类似的东西来做这种事情的好方法。

特别是,customConfig: Configuration似乎正是最后一段所指的参数类型(例如,不是类型类实例的隐式参数

我相信@travis-brown 或任何其他 Circe 的主要贡献者可以对此有所了解,以防实际上有办法做到这一点 - 我很高兴知道它!:)

于 2017-03-29T15:35:38.403 回答