在Scala中将一个Case类转换为CSV

9
有没有一种优雅的方式将一个case class转换为CSV值。
例如:
 case class Person( name : String, age : Int, gender: String, address : Option[String])

我在考虑使用宏,但想知道是否有其他替代方案。
注意:这个case类不包含任何用户定义的字段。
4个回答

17

那么 implicitsproductIterator 呢?

implicit class CSVWrapper(val prod: Product) extends AnyVal {
    def toCSV() = prod.productIterator.map{
                    case Some(value) => value
                    case None => ""
                    case rest => rest
                  }.mkString(",")
}

Person("name", 30, "male", None).toCSV()

1
好主意,但需要适当转义 "scala> Person("\"", 30, "male", None).toCSV() res1: String = ",30,male, - yǝsʞǝla
3
@AlekseyIzmailov,我会把那些细节问题留给实现者处理,因为也许他们的用例允许这样做。 - Justin Pihony
value.toString.replace("\"", "\\\"") 是什么意思? - John

11

是的,在Scala中,有一种方法可以将一个case class转换为CSV格式,而不需要添加任何模板。例如PureCSV,它基于令人惊叹的Shapeless库,可以实现此功能:

scala> import purecsv.safe._
scala> case class Interval(start: Long, end: Long)
scala> Interval(10,20).toCSV()
res1: String = 1,10
scala> Seq(Interval(1,10),Interval(11,20)).toCSV("|")
res2: String =
1|10
11|20

注意:我是PureCSV的作者。

1
我更喜欢PureCSV而不是被接受的答案,因为正确处理CSV数据完全取决于繁琐的解析细节。这些细节已经由http://opencsv.sourceforge.net/库处理好了,PureCSV基于此。此外,PureCSV还提供了良好的Scala集成。 - Patrick Refondini
2
PureCSV的问题在于它不适用于嵌套的case类。 - Loic
PureCSV不会根据案例类变量名创建CSV标题。 - Rick Moritz

2

product-collections 也能为您生成 CSV。该库具有很好的可扩展性;它是无需反射且与 Scala-js 兼容的。

import com.github.marklister.collections.io._
case class Foo(a:Int,b:String)
Seq(Foo(1,"hello"),Foo(2,"World")).csvIterator.mkString("\n")

res2: String =
1,"hello"
2,"World"

我是product-collections的作者


0
为了补充接受的答案,你还可以使用模式匹配来匹配产品类型:
implicit class CSVWrapper(val prod: Product) extends AnyVal {
  def toCSV: String = prod.productIterator.map{
    case p: Product =>  p.toCSV
    case rest => rest
  }.mkString(",")
}

这将适用于嵌套的 case classes:

case class Address(street: String, town: String, country: Option[String])
case class Person( name : String, age : Int, gender: Option[String], address : Option[Address])

Person("myname", 30, Some("male"), Some(Address("mystreet", "city", None))).toCSV
// result :
// myname,30,male,mystreet,city,

如果地址后面有一个空格,那么这将不是一个好的情况:None 将变成一个单独的空字段,跳过两列... - Stephen

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接