在 circe scala 中将 List[String] 解码为 List[JSONObject(key,value)]

Decode List[String] to List[JSONObject(key,value)] in circe scala

给定传入 json 如下所示,我如何根据条件在给定情况 class 中对其进行解码。

传入JSON

{
    "config": {
        "files": ["welcome"],
        "channel": "media"
    }
}

案例类

case class File(`type`: String, value: String)

case class Config(files: List[File],
                  channel: String = "BC")

object Config{
  implicit val FileDecoder: Decoder[File] = deriveDecoder[File]
  implicit val ConfigDecoder: Decoder[Config] = deriveDecoder[Config]
}

case class Inventory(config: Config)

object Inventory {
  implicit val InventoryDecoder: Decoder[Inventory] = deriveDecoder[Inventory]
}

我无法控制 json 中的传入文件值,它可以是 List[String] 或 List[File],所以我需要在我的解码逻辑中处理这两种情况。

因此,正如我们在上面看到的那样,我的目标是检查收入文件值是否为 List[String],然后将该值转换为以下类型硬编码为“音频”的位置

"files": [{
            "type": "audio",
            "value": "welcome.mp3"
        }],

整体 json 在映射到 case classes 以进行自动解码之前应该如下所示。

{
    "config": {
        "files": [{
            "type": "audio",
            "value": "welcome.mp3"
        }],
        "channel": "media"
    }
}

据我所知,这可以通过在解码前转换 json 来实现,也可以在文件解码期间实现。

我尝试在文件级别编写解码逻辑,但我无法成功。我没有明白如何做到这一点的关键。

尝试过的代码

  implicit val FileDecoder: Decoder[File] = deriveDecoder[File].prepare { (aCursor: ACursor) =>
  {

    if(!aCursor.values.contains("type")){
      aCursor.values.map( v =>
        Json.arr(
          Json.fromFields(
            Seq(
              ("type", Json.fromString("audio")),
              ("value", v.head)
            )
          )
        )
      )
    }
  }
  }

我们可以为 File 使用自定义 Decoder 来为 type

提供默认值
final case class File(`type`: String, value: String)
object File {
  implicit final val FileDecoder: Decoder[File] =
    Decoder.instance { cursor =>
      (
        cursor.getOrElse[String](k = "type")(fallback = "audio"),
        cursor.get[String](k = "value")
      ).mapN(File.apply)
    }.or(
      Decoder[String].map(value => File(`type` = "audio", value))
    )
}

可以这样使用:

val data =
"""[
  {
    "type": "audio",
    "value": "welcome.mp3"
  },
  {
    "value": "foo.mp3"
  },
  "bar.mp3"
]"""

parser.decode[List[File]](data)
// res: Either[io.circe.Error, List[File]] =
//  Right(List(
//    File("audio", "welcome.mp3"),
//    File("audio", "foo.mp3"),
//    File("audio", "bar.mp3")
//  ))

可以看到代码运行 here.