如何使用无形的方法从大平面csv文件中过滤和提取案例类对象?

时间:2017-06-13 04:58:26

标签: scala shapeless

我有一个大的平面非规范化csv文件,包含单行上的多个对象,如下所示:

a1, a2, a3, b1, b2, b3 ...
...

我有对象:

case class A(a1: Int, a2: String, a3: Float)
case class B...
...

并且遗留的是编写用于提取每个类的复杂适配器。我最近阅读了一些关于无形的讨论,我知道我可以用无形的通用编程解决这个问题。

甚至还有一个csv解析器示例,非常完美。我的想法是:

  1. 将csv解析为List [String]
  2. 使用对象字段信息
  3. 过滤ListString
  4. 使用已过滤的ListString并将其与Csv Parser示例
  5. 一起提供
  6. 因此我可以从csv文件中提取多行对象。
  7. 我遇到的问题:

    1. 我还在使用scala 2.10,我似乎已经正确配置了编译器插件(egg.mvn clean install正常工作)。但intellij偶尔无法编译,抛出异常。

      <groupId>org.scala-lang.plugins</groupId>
      <artifactId>macro-paradise_2.10</artifactId>
      <version>2.0.0-SNAPSHOT</version>
      
    2. 此代码来自shapeless示例

      implicit def deriveHConsOption[V, T <: HList](
          implicit
          scv: Lazy[CSVConverter[V]],
          sct: Lazy[CSVConverter[T]]
        ):CSVConverter[Option[V] :: T] = new CSVConverter[Option[V] :: T] {
          override def from(s: String): Try[shapeless.::[Option[V], T]] = s.span(_ != ',') 
      
    3. 但是我遇到了以下编译错误:

        

      错误:(70,28)::的类型参数错误,应为1
        ):CSVConverter [Option [V] :: T] = new CSVConverter [Option [V] :: T] {                             ^

      1. 我尝试使用无形过滤csv:

        //code to filter and extract one object 
        def extractCSVColumnsAndParse[T]:
            val labl = LabelledGeneric[T]
            val keys = Keys[labl.Repr].apply
            val keyNames = keys.toList.map(_.name)
        
      2. 然而,似乎T只能是具体的类型

          

        错误:(86,35)找不到参数lgen的隐含值:   shapeless.LabelledGeneric [T]           val labl = LabelledGeneric [T]

1 个答案:

答案 0 :(得分:1)

我对你的IntelliJ问题知之甚少,但我会给另外两个答案:

  • 2:::scala.collection.immutable.::List构造函数),除非您明确导入shapeless.::HList构造函数)。

  • 3:当你处理泛型类型时,你不能假设它们。它们可能是IntAnyMyCaseClassNothing,...因此编译器无法为它们找到LabelledGeneric(实际上,这将是什么LabelledGeneric的{​​{1}}?)。因此,您必须明确告诉您的所有泛型方法您的类型具有Any的实例,并且这是通过将其作为隐式参数(或上下文边界,在引擎盖下是相同的事物)来完成的。 。例如,你可以做到

    LabelledGeneric[T]

    然后,当您使用它时,对于显式案例类:

    // alternatively, def extractCSVColumnsAndParse[T: LabelledGeneric]
    def extractCSVColumnsAndParse[T](implicit ev: LabelledGeneric[T]) = {
      val labl = LabelledGeneric[T]
      val keys = Keys[labl.Repr].apply
      val keyNames = keys.toList.map(_.name)
      ...
    }
    

    无形的“魔力”是它为你生成extractCSVColumnsAndParse[MyCaseClass] //no need to pass the parameter, it is already in scope ,但它只能为特定类型(使用宏)这样做,所以你必须告诉编译器它存在,如果你'处理泛型类型。

修改

之后,您收到implicit ev: LabelledGeneric[MyCaseClass]错误,因为val keys的类型参数必须是Keys,因此您必须以某种方式强制执行此操作,因为{{1 }}不一定是HList。而且,您还需要提供隐式LabelledGeneric[T]#Repr,原因与HList相同。

Keys[Repr]

但是,这使得使用特定案例类调用变得不那么容易,因为您不能再执行LabelledGeneric。这是因为scala方法只有一个类型参数列表,所以你必须给它们全部或不给它们。

避免这种情况的一种复杂方法是​​以下模式,假设您的方法实际上有一些参数(例如,来自csv文件的def extractCSVColumnsAndParse[T, Repr <: HList](labl: LabelledGeneric.Aux[T, Repr], K: Keys[Repr]) { val keys = K() val keyNames = keys.toList.map(_.name) ... } 或csv文件路径):

extractCSVColumnsAndParse[MyCaseClass]

现在您可以使用

进行调用
List[String]

此模式允许您仅指定第一个类型参数,第二个参数在编译时推断。