如何从 Map[String, String] 实例化 case class
How instantiate case class from Map[String, String]
假设我有
case class Sample(i:Int, b:Boolean)
和
Map[String, String]("i" => "1", "b" => "false")
实例化任何情况的最简洁方法是什么class(如果所有字段都在映射中),签名如下:
get[T](map:[String, String]) : T
可能shapeless可以帮助完成这个任务,但我对它几乎不熟悉。谢谢!
首先你可以将Map[String, String]
转换成Map[Symbol, Any]
然后使用类型类shapeless.ops.maps.FromMap
(或扩展方法.toRecord
)和LabelledGeneric
:
import shapeless.LabelledGeneric
import shapeless.record.Record
import shapeless.syntax.std.maps._
case class Sample(i: Int, b: Boolean)
val rec = Map('i -> 1, 'b -> false).toRecord[Record.`'i -> Int, 'b -> Boolean`.T].get
LabelledGeneric[Sample].from(rec) //Sample(1,false)
如果不使用 Shapeless 是您的一个选项,您可以在没有它的情况下以简单的方式完成。这是一个示例实现,其中我使用 Try Monad 和模式匹配将 Map 转换为您的案例 class:
(Try("1".toInt), Try("false".toBoolean)) match {
case (Success(intVal), Success(boolVal)) =>
Sample(intVal, boolVal)
case _ => // Log and ignore the values
}
当然这比 Shapeless 版本有点冗长,但如果您不想只为这个简单的用例使用完整的库,您总是可以使用 Scala 库来完成!
伙计们。可能我不太简洁。我真正需要的是从 Map of Strings 到 case class 的通用解析器。感谢您指出 LabelledGeneric。这是解决方案:
import shapeless.labelled.{FieldType, field}
import shapeless.{::, HList, HNil, LabelledGeneric, Lazy, Witness}
object FieldParser {
trait ValParser[A] {
def parse(str:String): A
}
trait FieldParser[A] {
def parse: A
}
type FT[A, B] = FieldType[A, B]
type ListField[K <: Symbol, A] = FieldType[K, List[A]]
type FP[A] = FieldParser[A]
type Args = (List[String],Map[String, Int])
type Named[K <: Symbol] = Witness.Aux[K]
private def create[A](thunk: A): FP[A] = {
new FP[A] {
def parse: A = thunk
}
}
def apply[A](implicit st: Lazy[FP[A]]): FP[A] = st.value
implicit def genericParser[A, R <: HList](implicit generic: LabelledGeneric.Aux[A, R], parser: Lazy[FP[R]], args:Args): FP[A] = {
create(generic.from(parser.value.parse))
}
implicit def hlistParser[K <: Symbol, H, T <: HList](implicit hParser: Lazy[FP[FT[K, H]]], tParser: FP[T]): FP[FT[K, H] :: T] = {
create {
val hv = hParser.value.parse
val tv = tParser.parse
hv :: tv
}
}
implicit def standardTypeParser[K <: Symbol, V:ValParser](implicit named: Named[K], args:Args): FP[FieldType[K, V]] = {
create(field[K](implicitly[ValParser[V]].parse(findArg)))
}
implicit def optionParser[V](implicit valParser:ValParser[V]): ValParser[Option[V]] = new ValParser[Option[V]]{
def parse(str:String):Option[V] = {
str.isEmpty match {
case true => None
case false => Some(valParser.parse(str))
}
}
}
implicit def listParser[V](implicit valParser:ValParser[V]): ValParser[List[V]] = new ValParser[List[V]]{
def parse(str:String):List[V] = {
str.isEmpty match {
case true => Nil
case false => str.split(",").map(valParser.parse).toList
}
}
}
implicit def doubleParser: ValParser[Double] = new ValParser[Double]{
def parse(str:String):Double = str.toDouble
}
implicit def intParser: ValParser[Int] = new ValParser[Int]{
def parse(str:String):Int = str.toInt
}
implicit def strParser: ValParser[String] = new ValParser[String]{
def parse(str:String):String = str
}
implicit def boolParser: ValParser[Boolean] = new ValParser[Boolean]{
def parse(str:String):Boolean = str.toBoolean
}
implicit val hnilParser: FP[HNil] = create(HNil)
private def findArg[K <: Symbol](implicit args:Args, named: Named[K]): String = {
val name = named.value.name
val index = args._2(name)
args._1(index)
}
}
假设我有
case class Sample(i:Int, b:Boolean)
和
Map[String, String]("i" => "1", "b" => "false")
实例化任何情况的最简洁方法是什么class(如果所有字段都在映射中),签名如下:
get[T](map:[String, String]) : T
可能shapeless可以帮助完成这个任务,但我对它几乎不熟悉。谢谢!
首先你可以将Map[String, String]
转换成Map[Symbol, Any]
然后使用类型类shapeless.ops.maps.FromMap
(或扩展方法.toRecord
)和LabelledGeneric
:
import shapeless.LabelledGeneric
import shapeless.record.Record
import shapeless.syntax.std.maps._
case class Sample(i: Int, b: Boolean)
val rec = Map('i -> 1, 'b -> false).toRecord[Record.`'i -> Int, 'b -> Boolean`.T].get
LabelledGeneric[Sample].from(rec) //Sample(1,false)
如果不使用 Shapeless 是您的一个选项,您可以在没有它的情况下以简单的方式完成。这是一个示例实现,其中我使用 Try Monad 和模式匹配将 Map 转换为您的案例 class:
(Try("1".toInt), Try("false".toBoolean)) match {
case (Success(intVal), Success(boolVal)) =>
Sample(intVal, boolVal)
case _ => // Log and ignore the values
}
当然这比 Shapeless 版本有点冗长,但如果您不想只为这个简单的用例使用完整的库,您总是可以使用 Scala 库来完成!
伙计们。可能我不太简洁。我真正需要的是从 Map of Strings 到 case class 的通用解析器。感谢您指出 LabelledGeneric。这是解决方案:
import shapeless.labelled.{FieldType, field}
import shapeless.{::, HList, HNil, LabelledGeneric, Lazy, Witness}
object FieldParser {
trait ValParser[A] {
def parse(str:String): A
}
trait FieldParser[A] {
def parse: A
}
type FT[A, B] = FieldType[A, B]
type ListField[K <: Symbol, A] = FieldType[K, List[A]]
type FP[A] = FieldParser[A]
type Args = (List[String],Map[String, Int])
type Named[K <: Symbol] = Witness.Aux[K]
private def create[A](thunk: A): FP[A] = {
new FP[A] {
def parse: A = thunk
}
}
def apply[A](implicit st: Lazy[FP[A]]): FP[A] = st.value
implicit def genericParser[A, R <: HList](implicit generic: LabelledGeneric.Aux[A, R], parser: Lazy[FP[R]], args:Args): FP[A] = {
create(generic.from(parser.value.parse))
}
implicit def hlistParser[K <: Symbol, H, T <: HList](implicit hParser: Lazy[FP[FT[K, H]]], tParser: FP[T]): FP[FT[K, H] :: T] = {
create {
val hv = hParser.value.parse
val tv = tParser.parse
hv :: tv
}
}
implicit def standardTypeParser[K <: Symbol, V:ValParser](implicit named: Named[K], args:Args): FP[FieldType[K, V]] = {
create(field[K](implicitly[ValParser[V]].parse(findArg)))
}
implicit def optionParser[V](implicit valParser:ValParser[V]): ValParser[Option[V]] = new ValParser[Option[V]]{
def parse(str:String):Option[V] = {
str.isEmpty match {
case true => None
case false => Some(valParser.parse(str))
}
}
}
implicit def listParser[V](implicit valParser:ValParser[V]): ValParser[List[V]] = new ValParser[List[V]]{
def parse(str:String):List[V] = {
str.isEmpty match {
case true => Nil
case false => str.split(",").map(valParser.parse).toList
}
}
}
implicit def doubleParser: ValParser[Double] = new ValParser[Double]{
def parse(str:String):Double = str.toDouble
}
implicit def intParser: ValParser[Int] = new ValParser[Int]{
def parse(str:String):Int = str.toInt
}
implicit def strParser: ValParser[String] = new ValParser[String]{
def parse(str:String):String = str
}
implicit def boolParser: ValParser[Boolean] = new ValParser[Boolean]{
def parse(str:String):Boolean = str.toBoolean
}
implicit val hnilParser: FP[HNil] = create(HNil)
private def findArg[K <: Symbol](implicit args:Args, named: Named[K]): String = {
val name = named.value.name
val index = args._2(name)
args._1(index)
}
}