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

Posted

技术标签:

【中文标题】如何使用 circe 将密封特征案例对象转换为字符串【英文标题】:How to convert sealed trait case objects to string using circe 【发布时间】:2020-03-23 22:51:00 【问题描述】:

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

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

当调用SessionModel.Authentication 时,此案例对象的输出如下:

"Authentication":

我需要将其转换为字符串,以便输出"authentication"

【问题讨论】:

您可以在添加the circe-generic-extra dependency 后使用here 之类的代码派生解码器和编码器。 另外,为了让名称保持小写,您可以使用自定义名称转换器:implicit val (modeDecoder, modeEncoder) = implicit val config: Configuration = Configuration.default.withDefaults.copy(transformConstructorNames = _.toLowerCase); (deriveEnumerationDecoder[Mode],deriveEnumerationEncoder[Mode]) 【参考方案1】:

正如 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 之外不需要任何东西,不那么神奇,编译速度更快等。泛型派生对于具有直接映射的简单案例类非常有用,但我认为人们经常尝试将其扩展以涵盖所有情况,因为手动编写实例不会造成太大负担,甚至可能更清晰。

【讨论】:

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

以上是关于如何使用 circe 将密封特征案例对象转换为字符串的主要内容,如果未能解决你的问题,请参考以下文章

使用 Circe 将包含 HList 的案例类解析为 JSON 字符串

使用密封的特征/案例类播放JSON:无限递归

无法使用Circe JSON解析器创建遍历JSON字符串的对象

Scala Play Framework 2.6密封特征格式给Json

将circe中json对象的所有键从“下划线”转换为“驼峰式”

Scala使用circe将None编码为NaN json值