如何使用circe将密封的特质案例对象转换为字符串

17

我正在使用Scala和Circe。我有以下密封特质。

  sealed trait Mode
  case object Authentication extends Mode
  case object Ocr extends Mode

调用此case object时,SessionModel.Authentication 的输出如下:

"Authentication":{}

我需要将这个转换为字符串,使其输出"authentication"


2
此外,为了保持名称小写,您可以使用自定义名称转换器:implicit val(modeDecoder,modeEncoder)= { implicit val config:Configuration = Configuration.default.withDefaults.copy(transformConstructorNames = _.toLowerCase); (deriveEnumerationDecoder [Mode],deriveEnumerationEncoder [Mode]) } - Andriy Plokhotnyuk
1个回答

21

正如Andriy Plokhotnyuk上面提到的那样,您可以使用circe-generic-extras:

import io.circe.Codec
import io.circe.generic.extras.Configuration
import io.circe.generic.extras.semiauto.deriveEnumerationCodec

sealed trait Mode
case object Authentication extends Mode
case object Ocr extends Mode

object Mode {
  private implicit val config: Configuration =
    Configuration.default.copy(transformConstructorNames = _.toLowerCase)

  implicit val modeCodec: Codec[Mode] = deriveEnumerationCodec[Mode]
}

然后:

scala> import io.circe.syntax._
import io.circe.syntax._

scala> (Authentication: Mode).asJson
res1: io.circe.Json = "authentication"

scala> io.circe.Decoder[Mode].decodeJson(res1)
res2: io.circe.Decoder.Result[Mode] = Right(Authentication)

(请注意,Codec是0.12版中的新功能——对于早期版本,您必须像Andriy的评论中那样编写两个实例。)

不过,除非您需要维护很多这样的实例,我个人认为手动编写实例通常比使用circe-generic-extras更好,在这种情况下,手动编写实例甚至不会更加冗长:

import io.circe.{Decoder, Encoder}

sealed trait Mode
case object Authentication extends Mode
case object Ocr extends Mode

object Mode {
  implicit val decodeMode: Decoder[Mode] = Decoder[String].emap {
    case "authentication" => Right(Authentication)
    case "ocr"            => Right(Ocr)
    case other            => Left(s"Invalid mode: $other")
  }

  implicit val encodeMode: Encoder[Mode] = Encoder[String].contramap {
    case Authentication => "authentication"
    case Ocr            => "ocr"
  }
}

这个版本的工作原理与deriveEnumerationCodec版本完全相同,但不需要任何东西,只需使用circe-core,更少神秘,编译速度更快等等。对于简单情况下具有直接映射的简单案例类,泛型衍生可以非常好用,但我认为人们经常试图将其扩展以涵盖所有情况,而手动编写实例并不会太麻烦,甚至可能更清晰明了。


从circe 0.12.2开始,隐式配置似乎不会自动捕获,因此只有第二种“手动”方法适用于我。如果您扩展Product和Serializable:sealed trait Mode extends Product with Serializable,则可以使用更少的样板生成字符串:implicit val encodeMode: Encoder[Mode] = Encoder[String].contramap { _.productPrefix.toLowerCase } - Sotomajor
我也喜欢第二种方法,但是由于没有详尽的模式匹配,你怎么知道是否忘记了一个字符串?另一种方法是在你的特质的伴生对象中,保存所有子类的列表,并创建一个 def 函数,根据它们必须继承的字符串 id 查找相关的子类型。然后,您可以在单元测试中使用详尽的模式匹配来确保始终包括所有情况。这个 def 函数可以在编解码器中使用,或者通常在运行时使用。该 id 是您可以序列化的字符串表示形式。 - ecoe

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接