我正在使用shapeless编写一个泛型函数,该函数采用case class
的实例并修剪字符串字段的所有值。 case类可以具有可选字段,嵌套对象,列表等。
我有一个案例类Person
。
case class Person(name: Option[String], address: List[String], friends: List[Person])
我当前拥有的功能:
import shapeless._, ops.hlist._
object trimmer extends Poly1 {
implicit val stringOptCase = at[Option[String]](_.map(_.trim))
implicit val stringListCase = at[List[String]](_.map(_.trim))
implicit def skipCase[A] = at[A](identity)
}
def trimStringValues[A, R <: HList](a: A)(implicit
gen: Generic.Aux[A, R],
mapper: Mapper.Aux[trimmer.type, R, R]
) = gen.from(mapper(gen.to(a)))
当我使用上述函数时,它仅适用于类name
的根级别Person
字段。它不适用于列表或对象字段。
val person = Person(name = Some(" john "), address = List(" ny"," vegas "), friends = List(Person(Some(" alicia"), List(" peter"), Nil)))
trimStringValues(person) // Person(Some(john),List(ny, vegas),List(Person(Some( alicia),List( peter),List())))
我该如何解决?
答案 0 :(得分:4)
首先,它似乎在您评论的输出中的address
和name
上都可以正常工作。它不适用于friends
,因为List[Person]
与skipCase
大小写匹配-它既不是Option[String]
也不是List[String]
。
解决此问题的最简单方法是使用Shapeless的everywhere
组合器。鉴于上面的代码,您可以编写以下代码:
scala> shapeless.everywhere(trimmer)(person)
res1: Person = Person(Some(john),List(ny, vegas),List(Person(Some(alicia),List(peter),List())))
实际上,您可以使用更简单的trimmer
实现来完成相同的事情:
object trimStrings extends Poly1 {
implicit val stringCase: Case.Aux[String, String] = at[String](_.trim)
}
或者等效但更简洁:
import shapeless.poly.->
object trimStrings extends (String -> String)(_.trim)
然后:
scala> shapeless.everywhere(trimStrings)(person)
res5: Person = Person(Some(john),List(ny, vegas),List(Person(Some(alicia),List(peter),List())))
如果您想进一步控制要精确修剪的字符串,则可以返回到原始实现,并添加一个显式的List[Person]
大小写,或者添加一个更通用的大小写,该大小写与此类匹配并应用{{1 }}递归地。既然您说要修剪所有字符串,trimmer
听起来就是您想要的。