已配置蛇形ADT的JsonCodec

问题描述

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

解决方法

所有用于副产物的Circe推导方法都假定存在判别字段,充其量您可以选择其名称。因此支持的格式如下:

{
  "type": "foo_bar",}

而不只是

"foo_bar"

原因是,仅案例对象类与案例对象和案例类之间的派生机制没有区别,因此对于此类代码

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

您只需组合求和部分的编解码器

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

即使您对Value1进行了编码,您也始终会解码到第一个Value2。派生认为这两种情况之间没有区别,因此,他们倾向于始终使用区分字段来保持安全。 (这是discussed in docs)。

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

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
}

这可以实现您所期望的目标,尽管采用不同的方式。

相关问答

错误1:Request method ‘DELETE‘ not supported 错误还原:...
错误1:启动docker镜像时报错:Error response from daemon:...
错误1:private field ‘xxx‘ is never assigned 按Alt...
报错如下,通过源不能下载,最后警告pip需升级版本 Requirem...