将嵌套对象编码为字符串

Encode nested object as a string

给定以下情况类:

case class Mailbox(value: String)
case class Group(objectType: String, mailbox: Mailbox)

我正在尝试找到一种方法来编码组对象,如下所示,其中 mailbox 被编码为字符串值,而不是对象:

{
  "objectType" : "Group",
  "mailbox" : "mailto:info@example.com"
}

通过自动推导,encoding/decoding 都成功了,但我最终得到的结果如下所示:

{
  "objectType" : "Group",
  "mailbox" : {
    "value" : "mailto:info@example.com"
  }
}

我可以通过添加如下自定义编码器来实现我想要的结果:

object Mailbox {
  implicit val encoder: Encoder[Mailbox] = (m: Mailbox) => Json.fromString(m.value)
  implicit val decoder: Decoder[Mailbox] = deriveDecoder[Mailbox]
}

但是,解码失败并显示以下内容:

DecodingFailure(Attempt to decode value on failed cursor, List(DownField(value), DownField(mailbox)))

我也试图通过为邮箱编写自定义解码器来解决这个问题,但得到了相同的结果。任何有关处理这种情况的正确方法的指导将不胜感激。

完整代码如下:

case class Mailbox(value: String)
object Mailbox {
  implicit val encoder: Encoder[Mailbox] = (m: Mailbox) => Json.fromString(m.value)
  implicit val decoder: Decoder[Mailbox] = deriveDecoder[Mailbox]
}

case class Group(objectType: String, mailbox: Mailbox)

object Sandbox {
  def main(args: Array[String]): Unit = {

    val group: Group = Group("Group", Mailbox("mailto:info@example.com"))
    val json: String = group.asJson.spaces2
    println(json)

    parser.decode[Group](json) match {
      case Right(group) => println(group)
      case Left(err) => println(err)
    }
  }
}

请注意,这是派生示例,仅用于演示我的问题。

您可以使用 map / contramap 来将您的类型映射到 String 并返回:

object Mailbox {

  implicit val encoder: Encoder[Mailbox] = Encoder.encodeString.contramap[Mailbox](_.value)
  implicit val decoder: Decoder[Mailbox] = Decoder.decodeString.map[Mailbox](Mailbox.apply)
}

几乎完全描述这种情况的文档位于 Custom encoders/decoders

作为创建自定义 encoders/decoders 的替代方法,您还可以使用 circe extras 中的 deriveUnwrappedEncoder/deriveUnwrappedDecoder

只需在你的代码中添加以下导入即可 buid.sbt:

libraryDependencies += "io.circe" %% "circe-generic-extras" % "xxx"

然后你将能够做到:

import io.circe.generic.extras.semiauto._

implicit val encoder: Encoder[Mailbox] = deriveUnwrappedEncoder
implicit val decoder: Decoder[Mailbox] = deriveUnwrappedDecoder