如何解决使用 magnolia 时的问题 "could not find implicit value for evidence parameter of type"

How to fix an issue "could not find implicit value for evidence parameter of type" when using magnolia

我正在尝试制作一个 CSV(带有 headers)解析器,将一行提取到 case class 中。我希望提取依赖 header 来影响(而不是依赖 case class 参数与 CSV 中的顺序相同)正确字段的值。我正在使用 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)
                                                       } )
}

它深受 caesura 的启发。

编译的时候报错:

[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]

我做错了什么?

implicit val strLineDecoder: LineDecoder[String] = ??? // your implementation

LineDecoder 的伴生对象。

Magnolia 可以为 case classes 和 sealed traits 派生类型 class 的实例,但不能为其他类型猜测它们。

另外 def extract[T: LineDecoder : ClassTag](): Array[T] = ... 应该代替 def extract[T: LineDecoder](): ArraySeq[T] = ...