Scala无形状键入映射[Symbol,String]和case类
我正在读取查询参数并将它们转换为Scala无形状键入映射[Symbol,String]和case类,scala,shapeless,Scala,Shapeless,我正在读取查询参数并将它们转换为映射[符号,字符串]。我想通过一组case类向这些查询参数添加一些类型安全性 根据传入的http请求,这些案例类将有所不同,因此需要支持不同的案例类 如果传入的查询参数与定义的case类不匹配,解析器应返回None 我尝试使用shapeless实现一个通用解析器。如果所有参数的类型都是String,则该方法有效。但是我需要支持任何类型的查询参数 我曾尝试将本文中看到的隐式转换逻辑结合起来,但无法使其正常工作。 (不成形的新事物) 现有的解析器(无字符串到类型的转换
映射[符号,字符串]
。我想通过一组case类向这些查询参数添加一些类型安全性
根据传入的http请求,这些案例类将有所不同,因此需要支持不同的案例类
如果传入的查询参数与定义的case类
不匹配,解析器
应返回None
我尝试使用shapeless实现一个通用解析器。如果所有参数的类型都是String
,则该方法有效。但是我需要支持任何类型的查询参数
我曾尝试将本文中看到的隐式转换逻辑结合起来,但无法使其正常工作。
(不成形的新事物)
现有的解析器
(无字符串到类型的转换):
类解析器[A]{
来自[R sampleVersion.toString]的def
val result=Parser.to[QueryParams].from(mapOfQueryParams)
结果应该是“定义的”
result.get.name应等于sampleName
result.get.version应等于sampleVersion.toString
}
它应该在中“从具有任何类型值的映射解析查询参数”{
val mapOfQueryParams=Map('name->sampleName,'version->sampleVersion.toString)
val result=Parser.to[QueryParams2]。from(mapOfQueryParams)
//未定义结果,因为它无法将字符串转换为整数
结果应该是“定义的”
result.get.name应等于sampleName
result.get.version应等于sampleVersion
}
}
FromMap
使用无形状.可键入的
将值转换为预期的类型。因此,使代码正常工作的最简单方法是定义一个可键入的
实例,将字符串
转换为Int
(以及出现在案例类中的任何值类型的附加Typeable
实例):
然而,这不是Typeable
的预期用途,它旨在确认类型为Any
的变量在没有任何转换的情况下已经是预期类型的实例。换句话说,它是asInstanceOf
的类型安全实现,也可以解决类型擦除问题
为确保正确性,您可以定义自己的
ReadFromMap
typeclass,它使用自己的Read
typeclass从String
s转换为预期类型。下面是Read
typeclass的一个简单实现(假设Scala 2.12):
您可以复制并调整FromMap
的实现,以使用此Read
typeclass:
import shapeless._
import shapeless.labelled._
trait ReadFromMap[R <: HList] extends Serializable {
def apply(map: Map[Symbol, String]): Option[R]
}
object ReadFromMap {
implicit def hnil: ReadFromMap[HNil] = _ => Some(HNil)
implicit def hlist[K <: Symbol, V, T <: HList](implicit
keyWitness: Witness.Aux[K],
readValue: Read[V],
readRest: ReadFromMap[T]
): ReadFromMap[FieldType[K, V] :: T] = map => for {
value <- map.get(keyWitness.value)
converted <- readValue(value)
rest <- readRest(map)
} yield field[K](converted) :: rest
}
import scala.util.Try
trait Read[T] {
def apply(string: String): Option[T]
}
object Read {
implicit val readString: Read[String] = Some(_)
implicit val readInt: Read[Int] = s => Try(s.toInt).toOption
// Add more implicits for other types in your case classes
}
import shapeless._
import shapeless.labelled._
trait ReadFromMap[R <: HList] extends Serializable {
def apply(map: Map[Symbol, String]): Option[R]
}
object ReadFromMap {
implicit def hnil: ReadFromMap[HNil] = _ => Some(HNil)
implicit def hlist[K <: Symbol, V, T <: HList](implicit
keyWitness: Witness.Aux[K],
readValue: Read[V],
readRest: ReadFromMap[T]
): ReadFromMap[FieldType[K, V] :: T] = map => for {
value <- map.get(keyWitness.value)
converted <- readValue(value)
rest <- readRest(map)
} yield field[K](converted) :: rest
}
class Parser[A] {
def from[R <: HList]
(m: Map[Symbol, String])
(implicit
gen: LabelledGeneric.Aux[A, R],
fromMap: ReadFromMap[R]
): Option[A] = fromMap(m).map(gen.from)
}