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)
}