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

使用Shapeless派生Case Class的查询字符串参数(含特殊列表编码)

解决Case Class列表字段的特殊查询字符串编码问题

嘿,我明白你的需求了——要把case class里的List字段编码成fieldName.1=value&fieldName.2=value这种格式,而不是常规的数组格式。咱们基于你现有的Encoder类型类来扩展,思路很清晰,就是给List类型的字段专门写一个更具体的HList编码器,让编译器优先选用它。

具体修改步骤

首先,在你的Encoder伴生对象里,添加一个针对List类型字段的HList编码器:

implicit def listHlistEncoder[K <: Symbol, V, T <: HList](
  implicit witness: Witness.Aux[K],
  elementEncoder: Lazy[Encoder[V]],
  tEncoder: Encoder[T]
): Encoder[FieldType[K, List[V]] :: T] = {
  val fieldName = witness.value.name
  createEncoder { hlist =>
    // 处理List字段,生成带索引的键值对
    val listEncoded = hlist.head.zipWithIndex.map { case (value, idx) =>
      s"$fieldName.${idx + 1}=${elementEncoder.value.encode(value)}"
    }.mkString("&")
    // 处理剩余的HList
    val tailEncoded = tEncoder.encode(hlist.tail)
    // 拼接逻辑,避免多余的&符号
    (listEncoded, tailEncoded) match {
      case ("", "") => ""
      case ("", t) => t
      case (l, "") => l
      case (l, t) => s"$l&$t"
    }
  }
}

然后保留你原来的通用HList编码器、Generic编码器和基础类型编码器即可。

为什么这样做?

Scala的隐式解析会优先选择更具体的类型实例。你的通用hlistEncoder是处理任意FieldType[K, H] :: T的,而新的listHlistEncoder是专门处理FieldType[K, List[V]] :: T的,所以当HList的头部是List类型的字段时,编译器会自动选用这个专门的编码器。

测试你的例子

现在用你给的Example类测试:

case class Example(attributes: List[String])
val example = Example(List("foo", "bar"))
encode(example) // 输出: "attributes.1=foo&attributes.2=bar"

完美符合你的需求!

边缘情况处理

如果需要处理空列表的情况(比如空列表不生成任何键值对),可以在listEncoded的生成逻辑里加个判断:

val listEncoded = if (hlist.head.isEmpty) "" 
                  else hlist.head.zipWithIndex.map { case (value, idx) =>
                    s"$fieldName.${idx + 1}=${elementEncoder.value.encode(value)}"
                  }.mkString("&")

这样当列表为空时,这个字段就不会出现在查询字符串里了。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.06 20:17:45