使用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
相关产品推荐
相关产品推荐

