Skip to content

underscoreio/csvside

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

79 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

CSVSide

CSV readers and combinators for Scala. Made with Cats.

Copyright 2015-2020 Richard Dallaway and Dave Gurnell. Licensed Apache 2.

Build Status Coverage status Maven Central

Getting Started

Grab the code by adding the following to your build.sbt:

libraryDependencies += "io.underscore" %% "csvside" % "<<VERSION>>"

For Scala 2.11 and 2.12, use version 1.0.0.

Synopsis

Fixed Reader Row CSV Files

Here's an example that reads directly from a String. You can also read from a java.io.File or a java.io.Reader:

// We want to parse this CSV...
val csv = """
  |Str,Bool,Int
  |abc,true,123
  |"a b",false,321
  |,,
  |abc,abc,abc
""".trim.stripMargin

// To a sequence of this data structure...
case class Test(str: String, num: Int, bool: Option[Boolean])

// We import Csvside...
import csvside._

// We define a RowFormat...
import cats.syntax.cartesian._
implicit val testFormat: RowFormat[Test] = (
  "Str".csv[String] |@|
  "Int".csv[Int] |@|
  "Bool".csv[Option[Boolean]]
).imap(Test.apply)(unlift(Test.unapply))

// We read the data...
val ans = Csv.fromString[Test](csv).toList
// ans: List[csvside.CsvValidated[Test]] = List(
//   Valid(Test(abc,123,Some(true))),
//   Valid(Test(a b,321,Some(false))),
//   Invalid(List(CsvError(4,Int,Must be a whole number))),
//   Invalid(List(CsvError(5,Int,Must be a whole number),
//                CsvError(5,Bool,Must be a yes/no value or blank))))

// And we write it back to CSV...
import cats.data.Validated
val validOnly = ans collect { case Validated.Valid(test) => test }
val finalCsv = Csv.toString(validOnly)
// finalCsv: String =
// ""Str","Int","Bool"
// "abc","123","true"
// "a b","321","false"
// "

Variable Header Row CSV Files

If the format of the rows depends on the values in the header row, we can use a ListReader[A] to generate a ColumnReader[A] on the fly:

// We want to parse the cells in this CSV...
val csv = s"""
  |Row,Col1,Col2,Col3
  |x,1,2,3
  |y,,,
  |z,3,,1
""".trim.stripMargin

// To a sequence of this data structure...
case class Test(key: String, values: Map[CsvPath, Option[Int]])

// We do this by creating a `ListReader` that
// parses the column headings and creates a `ColumnReader`
// to read the rest of the file.

// We import from Csvside...
import csvside._

// We define a ListReader...
import cats.data.Validated.{valid, invalid}
import cats.syntax.cartesian._
import cats.syntax.validated._
implicit val testReader: ListReader[Test] =
  ListReader[Test] {
    case head :: tail =>
      (head.read[String] |@| tail.readMap[Option[Int]]).map(Test.apply).valid

    case Nil =>
      invalid(List(CsvError(1, "", "File must contain at least one column")))
  }

// And we read the data...
val ans = Csv.fromString[Test](csv).toList
// ans: List[CsvValidated[Test]] =
//   List(
//     Valid(Test("x", Map("Col1" -> Some(1), "Col2" -> Some(2), "Col3" -> Some(3)))),
//     Valid(Test("y", Map("Col1" -> None,    "Col2" -> None,    "Col3" -> None))),
//     Valid(Test("z", Map("Col1" -> Some(3), "Col2" -> None,    "Col3" -> Some(1)))))

About

CSV reader and writer combinators for Scala. Made with Cats.

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages