json - 解码json字符串里面的json

标签 json scala circe

我正在处理 an API它需要一个 JSON 对象,其中一个值 (blob) 是字符串化的 JSON 对象:

{
    "credential": {
        "blob": "{\"access\":\"181920\",\"secret\":\"secretKey\"}",
        "project_id": "731fc6f265cd486d900f16e84c5cb594",
        "type": "ec2",
        "user_id": "bb5476fd12884539b41d5a88f838d773"
    }
}

我的域类是:
case class Credential(access: String, secret: String, projectId: String, userId: String)

编码域类很容易:
implicit val encoder: Encoder[Credential] = (a: Credential) => Json.obj(
  "type" -> "ec2".asJson,
  "blob" -> Map("access" -> a.access, "secret" -> a.secret).asJson.noSpaces.asJson,
  "project_id" -> a.projectId.asJson,
  "user_id" -> a.userId.asJson
)

但是解码要困难得多:
implicit val decoder: Decoder[Credential] = (c: HCursor) => for {
  blobJsonString <- c.get[String]("blob")
  blob <- decode[Json](blobJsonString).left.map(e => DecodingFailure(e.getMessage, c.downField("blob").history))
  access <- blob.hcursor.get[String]("access")
  secret <- blob.hcursor.get[String]("secret")
  projectId <- c.get[String]("project_id")
  userId <- c.get[String]("user_id")
} yield Credential(access, secret, projectId, userId)

我不喜欢这个实现,因为它迫使我依赖 circe-parser,并打破编码器/解码器提供的抽象层。

有没有办法实现一个以一般方式进行双重解码的解码器?

最佳答案

好吧,因为所描述的 JSON 并不是真正的典型案例,我不确定是否有可能完全避免手动解析,但是如果您将更改表示此结构的案例类,您可以利用 circe 提供的一些优势。
请在下面找到代码示例:

import io.circe._
import io.circe.generic.semiauto._
import io.circe.generic.auto._

object CredentialsParseApp {
  case class CredentialsBlob(access: String, secret: String)

  object CredentialsBlob {

    implicit val encoder: Encoder[CredentialsBlob] = {
      val derivedEncoder: Encoder[CredentialsBlob] = deriveEncoder[CredentialsBlob]
      Encoder[String].contramap(blob => derivedEncoder(blob).noSpaces)
    }

    implicit val decoder: Decoder[CredentialsBlob] = {
      val derivedDecoder: Decoder[CredentialsBlob] = deriveDecoder[CredentialsBlob]
      Decoder[String].emap { value =>
        for {
          json <- parser.parse(value).left.map(_.message)
          blob <- json.as(derivedDecoder).left.map(_.message)
        } yield blob
      }
    }
  }

  case class Credentials(blob: CredentialsBlob, project_id: String, `type`: String = "ec2", user_id: String)
  case class Response(credential: Credentials)

  def main(args: Array[String]): Unit = {
    val jsonString =
      """{
         |    "credential": {
         |        "blob": "{\"access\": \"181920\", \"secret\": \"secretKey\" }",
         |        "project_id": "731fc6f265cd486d900f16e84c5cb594",
         |        "type": "ec2",
         |        "user_id": "bb5476fd12884539b41d5a88f838d773"
         |    }
         |}""".stripMargin

    println(parser.parse(jsonString).flatMap(_.as[Response]))
  }
}

在我的情况下产生了下一个结果:
Right(Response(Credentials(CredentialsBlob(181920,secretKey),731fc6f265cd486d900f16e84c5cb594,ec2,bb5476fd12884539b41d5a88f838d773)))

我在这个例子中使用了 circe 版本“0.12.3”。希望这可以帮助!

关于json - 解码json字符串里面的json,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60045035/

相关文章:

json - 在circe中合并几个json数组

java - Android - JSON 解析未使用 "setText"显示数据

javascript - 为什么 getBoundingClientRect 不使用 JSON.to Stringify 反序列化?

python - 加扰的 JSON 字符串

Scala:函数组合中的类型不匹配,发现 (Int, Int) => Seq[Int] require ? => 序列[整数]

scala - ZIO环境 build

scala - 在 scala 中使用 circe 时如何忽略序列化字段

json - Golang从Postgres获取原始JSON

scala - Scala 中 println 的替代方案

scala - 为什么它不解码为 ADT 类型?