You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Circe解码用户列表失败但单个用户正常的问题排查

问题:Circe解码User列表失败,单个User正常

我编写了两个测试用例:解码单个User的测试可正常运行,但解码User列表的测试却失败了。

测试代码如下:

import User._
import io.circe._
import io.circe.syntax._
import io.circe.parser.decode

class UserSuite extends munit.FunSuite:
  test("List of users can be decoded") {

    val json = """|{
                  |   "data" : [
                  |       {
                  |         "id" : "someId",
                  |         "name" : "someName",
                  |         "username" : "someusername"
                  |       },
                  |       {
                  |         "id" : "someId",
                  |         "name" : "someName",
                  |         "username" : "someusername"
                  |       }
                  |   ]
                  |}""".stripMargin    
    println(decode[List[User]](json))
  }

  test("user can be decoded") {
    val json = """|{
                  |   "data" : {
                  |         "id" : "someId",
                  |         "name" : "someName",
                  |         "username" : "someusername"
                  |       }
                  |}""".stripMargin
    println(decode[User](json))
  }

失败的测试输出:

Left(DecodingFailure(List, List(DownField(data))))

我的Decoder定义如下:

final case class User(
    id: String,
    name: String,
    username: String
)

object User:
    given Decoder[List[User]] = 
        deriveDecoder[List[User]].prepare(_.downField("data"))
    
    given Decoder[User] = 
        deriveDecoder[User].prepare(_.downField("data"))

环境为Scala 3.2.0和circe 0.14.1,请问这是Circe的Bug还是我的写法有误?


解答

这不是Circe的Bug,是你的写法有误。问题出在List[User]的解码器定义上:

  • deriveDecoder是Circe用来推导case class或产品类型解码器的工具,而List是容器类型,Circe已经内置了它的解码器实现,不需要(也不能)用deriveDecoder来生成。
  • 你写的deriveDecoder[List[User]]会生成一个错误的解码器,它试图把JSON内容当作一个case class来解析,而非列表,这就导致了解码失败。

修正方案

把List[User]的解码器定义改成直接使用Circe内置的Decoder[List[User]],再结合prepare定位到data字段:

final case class User(
    id: String,
    name: String,
    username: String
)

object User:
    given Decoder[User] = 
        deriveDecoder[User].prepare(_.downField("data"))
    
    // 修正List[User]的解码器
    given Decoder[List[User]] = 
        Decoder[List[User]].prepare(_.downField("data"))

或者更直观的写法,用Decoder.instance明确指定解码逻辑:

given Decoder[List[User]] = Decoder.instance { cursor =>
  cursor.downField("data").as[List[User]]
}

为什么单个User的解码正常?

因为User是case class,deriveDecoder[User]能正确生成它的解码器,再通过prepare(_.downField("data"))定位到嵌套的data字段,逻辑完全正确,所以解码成功。

内容的提问来源于stack exchange,提问作者TomPoczos

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.11 20:05:24