变体 json-field 的自定义 circe 解码器

Custom circe decoder for variant json-field

如何为 class 编写循环解码器

case class KeyValueRow(count: Int, key: String)

其中 json 包含字段 "count" (Int) 和一些额外的字符串字段(此字段的名称可能不同,例如 "url"、"city",随便什么)?

{"count":974989,"url":"http://google.com"}
{"count":1234,"city":"Rome"}

你可以像这样做你需要的:

import io.circe.syntax._
import io.circe.parser._
import io.circe.generic.semiauto._
import io.circe.{ Decoder, Encoder, HCursor, Json, DecodingFailure}

object stuff{
  case class KeyValueRow(count: Int, key: String)

  implicit def jsonEncoder : Encoder[KeyValueRow] = deriveEncoder

  implicit def jsonDecoder : Decoder[KeyValueRow] = Decoder.instance{ h =>
    (for{
      keys <- h.keys
      key <- keys.dropWhile(_ == "count").headOption
    } yield { 
      for{
        count <- h.get[Int]("count")
        keyValue <- h.get[String](key)
      } yield KeyValueRow(count.toInt, keyValue)
    }).getOrElse(Left(DecodingFailure("Not a valid KeyValueRow", List())))
  }
}

import stuff._

val a = KeyValueRow(974989, "www.google.com")

println(a.asJson.spaces2)

val test1 = """{"count":974989,"url":"http://google.com"}"""
val test2 = """{"count":1234,"city":"Rome", "will be dropped": "who cares"}"""

val parsedTest1 = parse(test1).flatMap(_.as[KeyValueRow])
val parsedTest2 = parse(test2).flatMap(_.as[KeyValueRow])

println(parsedTest1)
println(parsedTest2)

println(parsedTest1.map(_.asJson.spaces2))
println(parsedTest2.map(_.asJson.spaces2))

Scalafiddle:link here

正如我在上面的评论中提到的,请记住,如果您解码一些 json,然后 re-encode,结果将与初始输入不同。要解决此问题,您需要跟踪关键字段的原始名称。