我正在尝试制作一个 CSV(带标题)解析器,将行提取到case class
中。我希望提取依赖于标题来影响(而不是依赖于与 CSV 中的顺序相同的case class
参数)正确字段的值。我正在使用Magnolia来做反序列化部分。为了测试Magnolia,我向反序列化程序提供包含 CSV 内容的Map
。
我像这样调用解码器:
case class PlayerData(btag: String, team: String, status: String, role: String)
val csv = new CSV(Array(Map(
("btag" -> "btag#value"),
("team" -> "team_name"),
("status" -> "player_status"),
("role" -> "player role"),
)))
val ps = csv.extract[PlayerData]
for(PlayerData(b, t, _, r) <- ps) {
println(s"btag: $b, team: $t, role: $r")
}
解码器的实现如下:
object Decoding {
object LineDecoder {
implicit class DecoderOps[A: LineDecoder](p: Map[String, String]) {
def decode: A = implicitly[LineDecoder[A]].decode(p)
}
type Typeclass[T] = LineDecoder[T]
def combine[T](caseClass: CaseClass[LineDecoder, T]): LineDecoder[T] = new LineDecoder[T] {
def cols: Int = caseClass.parameters.map(_.typeclass.cols).sum
def decode(p: Map[String, String]): T = {
@annotation.tailrec
def make_param_list(row: Map[String, String],
cc_params: Seq[Param[Typeclass, T]],
c_params: Vector[Any]): T = {
if(cc_params.isEmpty) {
caseClass.rawConstruct(c_params)
} else {
val ctor_param = cc_params.head
val tail = cc_params.tail
val param_value = row.get(ctor_param.label).get
make_param_list(row, tail, c_params :+ param_value)
}
}
make_param_list(p, caseClass.parameters, Vector())
}
}
def apply[T](fn: Map[String, String] => T, len: Int = 1) = new LineDecoder[T] {
def decode(p: Map[String, String]): T = fn(p)
def cols: Int = len
}
implicit def gen[T]: LineDecoder[T] = macro Magnolia.gen[T]
}
trait LineDecoder[T] {
def cols: Int
def decode(p: Map[String, String]): T
}
}
class CSV(csv: Array[Map[String, String]]) {
import Decoding._
def extract[T: LineDecoder](): ArraySeq[T] = csv.map( line => {
implicitly[LineDecoder[T]].decode(line)
} )
}
它的灵感很大程度上来自凯撒拉。
编译时,我有这个错误:
[error] dev/scala/team-stats/src/main/scala/test.scala:67:25: could not find implicit value for evidence parameter of type Decoding.LineDecoder[CLI.PlayerData]
[error] val ps = csv.extract[PlayerData]
我做错了什么?
Put
implicit val strLineDecoder: LineDecoder[String] = ??? // your implementation
到LineDecoder
的同伴对象。
Magnolia 可以为案例类和密封特征派生类型类的实例,但无法猜测其他类型的实例。
也def extract[T: LineDecoder : ClassTag](): Array[T] = ...
应该是代替def extract[T: LineDecoder](): ArraySeq[T] = ...
.