将Scala中的CSV读入具有错误处理的类实例

我想在 Scala中读取CSV字符串/文件,以便给定一个case类C和一个错误类型Error,解析器填充一个Iterable [Either [Error,C]].有没有图书馆做这个或类似的东西?

例如,给定类和错误

case class Person(name: String,age: Int)

type Error = String

和CSV字符串

Foo,19
Ro
Bar,24

解析器将输出

Stream(Right(Person("Foo",1)),Left("Cannot read 'Ro'"),Right(Person("Bar",24)))

更新:

我认为我的问题不清楚,所以让我澄清一下:在Scala中有没有定义样板?给定任何案例类,有没有办法自动加载?我想以这种方式使用它:

val iter = csvParserFor[Person].parseLines(lines)

解决方法

这是一个无懈可击的实现,与 your proposed example中的方法略有不同.这是基于我过去写过的一些代码,与实现的主要区别在于这一点更通用 – 例如实际的CSV解析部分被分解出来,因此很容易使用专用的库.

首先为一个多用途的读类型类(no Shapeless yet):

import scala.util.{ Failure,Success,Try }

trait Read[A] { def reads(s: String): Try[A] }

object Read {
  def apply[A](implicit readA: Read[A]): Read[A] = readA

  implicit object stringRead extends Read[String] {
    def reads(s: String): Try[String] = Success(s)
  }

  implicit object intRead extends Read[Int] {
    def reads(s: String) = Try(s.toInt)
  }

  // And so on...
}

然后为有趣的部分:一个类型类,提供从列表的字符串转换(可能会失败)到HList:

import shapeless._

trait Fromrow[L <: HList] { def apply(row: List[String]): Try[L] }

object Fromrow {
  import HList.ListCompat._

  def apply[L <: HList](implicit fromrow: Fromrow[L]): Fromrow[L] = fromrow

  def fromFunc[L <: HList](f: List[String] => Try[L]) = new Fromrow[L] {
    def apply(row: List[String]) = f(row)
  }

  implicit val hnilFromrow: Fromrow[HNil] = fromFunc {
    case Nil => Success(HNil)
    case _ => Failure(new RuntimeException("No more rows expected"))
  }

  implicit def hconsFromrow[H: Read,T <: HList: Fromrow]: Fromrow[H :: T] =
    fromFunc {
      case h :: t => for {
        hv <- Read[H].reads(h)
        tv <- Fromrow[T].apply(t)
      } yield hv :: tv
      case Nil => Failure(new RuntimeException("Expected more cells"))
    }
}

最后让它与案例类一起工作:

trait RowParser[A] {
  def apply[L <: HList](row: List[String])(implicit
    gen: Generic.Aux[A,L],fromrow: Fromrow[L]
  ): Try[A] = fromrow(row).map(gen. from)
}

def rowParserFor[A] = new RowParser[A] {}

现在我们可以写下面的例子,例如使用OpenCSV

case class Foo(s: String,i: Int)

import au.com.bytecode.opencsv._
import scala.collection.JavaConverters._

val reader = new CSVReader(new java.io.FileReader("foos.csv"))

val foos = reader.readAll.asScala.map(row => rowParserFor[Foo](row.toList))

如果我们有一个这样的输入文件

first,10
second,11
third,twelve

我们会得到以下内容

scala> foos.foreach(println)
Success(Foo(first,10))
Success(Foo(second,11))
Failure(java.lang.NumberFormatException: For input string: "twelve")

(请注意,这引发了每一行的Generic和Fromrow实例,但是如果性能一个问题,那么很容易改变).

相关文章

共收录Twitter的14款开源软件,第1页Twitter的Emoji表情 Tw...
Java和Scala中关于==的区别Java:==比较两个变量本身的值,即...
本篇内容主要讲解“Scala怎么使用”,感兴趣的朋友不妨来看看...
这篇文章主要介绍“Scala是一种什么语言”,在日常操作中,相...
这篇文章主要介绍“Scala Trait怎么使用”,在日常操作中,相...
这篇文章主要介绍“Scala类型检查与模式匹配怎么使用”,在日...