【问题标题】:ConfiguredJsonCodec for snake case ADT用于蛇案例 ADT 的 ConfiguredJsonCodec
【发布时间】:2020-12-08 12:39:46
【问题描述】:

我必须使用一个 json api,其中有一个蛇案例中的离散字符串值列表。

以下示例有效,但我想删除手动 fooDecoder 以支持(当前已注释掉的)ConfiguredJsonCodec 注释。

或更基本的问题:将这些离散值建模为 ADT 中的案例对象是否有意义,或者是否有其他方法?

import io.circe._
import io.circe.syntax._
import io.circe.generic.extras.{Configuration, ConfiguredJsonCodec, JsonKey}
import io.circe.parser.parse

implicit val jsonConfig: Configuration = Configuration.default
  .withSnakeCaseConstructorNames
  .withSnakeCaseMemberNames

//@ConfiguredJsonCodec(decodeOnly = true)
sealed trait Foo
object Foo {
  case object FooBar extends Foo
  case object FooBaz extends Foo
  case object FooWuz extends Foo
}

import Foo._
implicit val fooDecoder: Decoder[Foo] = new Decoder[Foo] {
  override def apply(c: HCursor) = c.as[String].map{
    case "foo_bar" => FooBar
    case "foo_baz" => FooBaz
    case "foo_wuz" => FooWuz
  }
}

@ConfiguredJsonCodec(decodeOnly = true)
case class Qux(fooFoo: List[Foo])

val input ="""{"foo_foo" : ["foo_bar", "foo_baz", "foo_wuz"]}"""
val json: Json = parse(input).left.map(println(_)).right.get

json.as[Qux]

完整示例:https://scastie.scala-lang.org/eVFyNMGFRgaw9oEkRveT8g

这使用大约 0.13.0

【问题讨论】:

    标签: json scala circe


    【解决方案1】:

    所有 Circe 联产品派生方法都假定存在歧视字段,您最多可以选择它的名称。所以支持的格式是这样的:

    {
      "type": "foo_bar",
    }
    

    而不仅仅是

    "foo_bar"
    

    原因是派生机制认为 case-object-only 和 case-object-and-case-classes 之间没有区别,所以对于这样的代码

    trait MyADT
    object MyADT {
      case class Value1(value: String) extends MyADT
      case class Value2(value: String) extends MyADT
    }
    

    您只需将 sum 部分的编解码器组合在一起

    val decoder: Decoder[MyADT] = Decoder[Value1].widen orElse Decoder[Value2].widen
    

    即使您对Value2 进行了编码,您也总是会首先解码为Value1。 Derivation 认为这两种情况之间没有区别,因此他们更愿意始终使用判别场来保证安全。 (这里是discussed in docs)。

    但是,如果您知道自己正在构建枚举,则可以简单地使用带有 circe 支持的 enumeratum:

    import enumeratum._
    
    sealed trait Foo extends EnumEntry with Snakecase
    object Foo extends Enum[Foo] with CirceEnum[Foo] {
      case object FooBar extends Foo
      case object FooBaz extends Foo
      case object FooWuz extends Foo
    
      val values = findValues
    }
    

    这将实现您的目标,尽管方式不同。

    【讨论】:

    • 谢谢。不幸的是,我对我使用的 json 格式没有影响。不过我会考虑使用 enumeratum。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-02-15
    • 1970-01-01
    • 2017-05-08
    • 2020-02-10
    • 1970-01-01
    • 2021-06-22
    • 1970-01-01
    相关资源
    最近更新 更多