繁体   English   中英

使用circe解码JSON对象时捕获未使用的字段

[英]Capturing unused fields while decoding a JSON object with circe

假设我有一个类似下面的案例类,我想将一个JSON对象解码到其中,所有未使用的字段最终都留在剩余的特殊成员中:

import io.circe.Json

case class Foo(a: Int, b: String, leftovers: Json)

使用circe在Scala中执行此操作的最佳方法是什么?

(注意:我已经看过几次这样的问题 ,所以我为后人做了Q-and-A-ing。)

有几种方法可以解决这个问题。 一种相当简单的方法是过滤掉你在解码后使用的键:

import io.circe.{ Decoder, Json, JsonObject }

implicit val decodeFoo: Decoder[Foo] =
  Decoder.forProduct2[Int, String, (Int, String)]("a", "b")((_, _)).product(
    Decoder[JsonObject]
  ).map {
    case ((a, b), all) =>
      Foo(a, b, Json.fromJsonObject(all.remove("a").remove("b")))
  }

哪个可以按照您的预期工作:

scala> val doc = """{ "something": false, "a": 1, "b": "abc", "0": 0 }"""
doc: String = { "something": false, "a": 1, "b": "abc", "0": 0 }

scala> io.circe.jawn.decode[Foo](doc)
res0: Either[io.circe.Error,Foo] =
Right(Foo(1,abc,{
  "something" : false,
  "0" : 0
}))

这种方法的缺点是您必须维护代码以删除与其使用分开使用的密钥,这可能容易出错。 另一种方法是使用circe的state-monad驱动的解码工具:

import cats.data.StateT
import cats.instances.either._
import io.circe.{ ACursor, Decoder, Json }

implicit val decodeFoo: Decoder[Foo] = Decoder.fromState(
  for {
    a <- Decoder.state.decodeField[Int]("a")
    b <- Decoder.state.decodeField[String]("b")
    rest <- StateT.inspectF((_: ACursor).as[Json])
  } yield Foo(a, b, rest)
)

其工作方式与前一个解码器的工作方式相同(除了解码失败时您将获得的一些小错误):

scala> io.circe.jawn.decode[Foo](doc)
res1: Either[io.circe.Error,Foo] =
Right(Foo(1,abc,{
  "something" : false,
  "0" : 0
}))

后一种方法不需要您在多个位置更改已使用的字段,并且它还具有看起来更像您在其中手动编写的任何其他解码器的优点。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM