【问题标题】:How to Use Circe for Decoding JSON Lists/Arrays in Scala如何使用 Circe 在 Scala 中解码 JSON 列表/数组
【发布时间】:2017-07-06 10:47:23
【问题描述】:

我有代码 sn-p

cursor.downField("params").downField("playlist").downField("items").as[List[Clip]]

其中 Clip 是字符串和数字的简单案例类。传入的 Json 应该包含一个带有“项目”数组的 json 对象“播放列表”,其中每个项目都是一个剪辑。所以 json 应该是这样的

{
  "playlist": {
      "name": "Sample Playlist",
      "items": [
        {
          "clipId":"xyz", 
          "name":"abc"
        },
        {
          "clipId":"pqr", 
          "name":"def"
        } 
      ]
   }
}

使用上面的代码 sn-p,我得到了编译错误:

 Error:(147, 81) could not find implicit value for parameter d:     
 io.circe.Decoder[List[com.packagename.model.Clip]]
      cursor.downField("params").downField("playlist").downField("items").as[List[Clip]]

我做错了什么?如何使用 circe 为简单项目的列表/数组设置解码?

【问题讨论】:

  • 你能提供更多的上下文吗?错误是指一个参数d,那应该是什么?
  • 我认为是寻找解码器。一般来说,我的问题是如何将 json 数组解码为案例类对象的数组。
  • 您的做法似乎是正确的。您是否为 Clip 案例类声明了解码器(手动或(半)自动)?

标签: json scala circe


【解决方案1】:

为了完整起见,您可以创建一个自定义解码器,在其处理中包含导航,而不是导航到 JSON 值然后解码剪辑:

import io.circe.Decoder, io.circe.generic.auto._

case class Clip(clipId: String, name: String)

val decodeClipsParam = Decoder[List[Clip]].prepare(
  _.downField("params").downField("playlist").downField("items")
)

如果你有这个:

val json = """{ "params": {
  "playlist": {
      "name": "Sample Playlist",
      "items": [
        {
          "clipId":"xyz", 
          "name":"abc"
        },
        {
          "clipId":"pqr", 
          "name":"def"
        } 
      ]
   }
}}"""

你可以像这样使用解码器:

scala> io.circe.parser.decode(json)(decodeClipsParam)
res3: Either[io.circe.Error,List[Clip]] = Right(List(Clip(xyz,abc), Clip(pqr,def)))

我可能会更进一步,使用自定义案例类:

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

case class Clip(clipId: String, name: String)
case class PlaylistParam(name: String, items: List[Clip])

object PlaylistParam {
  implicit val decodePlaylistParam: Decoder[PlaylistParam] =
    deriveDecoder[PlaylistParam].prepare(
      _.downField("params").downField("playlist")
    )
}

现在你可以这样写了:

scala> io.circe.parser.decode[PlaylistParam](json).foreach(println)
PlaylistParam(Sample Playlist,List(Clip(xyz,abc), Clip(pqr,def)))

不过,您希望如何拆分导航和解码主要是个人喜好问题。

【讨论】:

  • 感谢您如此详尽的回复。为了更好地理解这一点,prepare 函数做了什么?什么时候使用比较合适。
  • @mattmar10 如果您将解码器视为从 JSON 值到某种类型 A 的管道,prepare 会将转换器附加到管道的输入端,以便您可以转换解码前的 JSON。
【解决方案2】:

感谢您的帮助。在离开一段时间并以新鲜的眼睛回来后,我能够弄清楚。

我认为我使用 downArray 函数出错了。

我的解决方案是执行以下操作:

override def main(args: Array[String]): Unit = {
    import ClipCodec.decodeClip

    val json = parse(Source.fromFile("playlist.json").mkString).right.get
    val clips = json.hcursor.downField("params").downField("playlist")
                   .downField("items").as[Seq[Clip]]

  }

【讨论】:

    【解决方案3】:

    Circe 正在为 List[Clip] 寻找隐式声明的解码器,但找不到它。

    我怀疑您没有手动或(半)自动定义解码器。您可以按照官方文档https://circe.github.io/circe/codec.html 进行这两项操作。

    很遗憾,我无法提供比这更详细的信息,因为这个问题相当模糊。当给出更多细节时,我会更新我的答案。

    【讨论】:

      猜你喜欢
      • 2021-11-11
      • 2017-10-03
      • 2019-10-30
      • 2019-03-12
      • 2021-11-07
      • 1970-01-01
      • 2019-04-06
      • 2020-07-06
      • 2019-06-03
      相关资源
      最近更新 更多