首页 > 解决方案 > 用于蛇案例 ADT 的 ConfiguredJsonCodec

问题描述

我必须使用一个 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

标签: jsonscalacirce

解决方案


所有 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

Value1即使您对Value2. Derivation 认为这两种情况之间没有区别,因此他们更愿意始终使用判别场来保证安全。(这在 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
}

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


推荐阅读