We’ve seen how kantan.csv uses encoders and decoders as a convenient way to support new types. This didn’t account for a fairly common scenario, however: types for which one wishes to declare both an encoder and a decoder. It’s certainly possible to write both, but it’s a bit cumbersome. kantan.csv offers an alternative: codecs, which are simply an encoder and a decoder rolled into one.
We’ve seen before how to create a CellEncoder and a
CellDecoder for joda’s DateTime
. CellCodec
allows you to do the same thing, but
in one go:
import kantan.csv._
import kantan.csv.ops._
import org.joda.time.DateTime
import org.joda.time.format.ISODateTimeFormat
implicit val jodaDateTime: CellCodec[DateTime] = {
val format = ISODateTimeFormat.date()
CellCodec.from(s => DecodeResult(format.parseDateTime(s)))(d => format.print(d))
}
And with that, we can now both encode and decode DateTime
:
val dates = List(
List(new DateTime(), new DateTime().plusDays(1)),
List(new DateTime().plusDays(2), new DateTime().plusDays(3))
).asCsv(rfc)
// dates: String = """2022-09-06,2022-09-07
// 2022-09-08,2022-09-09
// """
dates.readCsv[List, List[DateTime]](rfc)
// res0: List[ReadResult[List[DateTime]]] = List(
// Right(
// value = List(2022-09-06T00:00:00.000+02:00, 2022-09-07T00:00:00.000+02:00)
// ),
// Right(
// value = List(2022-09-08T00:00:00.000+02:00, 2022-09-09T00:00:00.000+02:00)
// )
// )
There’s a very similar mechanism for rows: RowCodec
. This one is a bit more powerful and a bit more complicated,
however: all the helper methods we’ve seen for creating RowDecoder
and RowEncoder
instances also exist for
RowCodec
. Let’s take a concrete example with case classes.
case class Person(id: Int, name: String, age: Int)
val ps = List(Person(0, "Nicolas", 38), Person(1, "Kazuma", 1), Person(2, "John", 18))
We want to be able to both encode and decode that, so we can create a RowCodec[Person]
instance:
implicit val personCodec: RowCodec[Person] = RowCodec.caseCodec(0, 2, 1)(Person.apply)(Person.unapply)
And with that one line, we’re done:
val csv = ps.asCsv(rfc)
// csv: String = """0,38,Nicolas
// 1,1,Kazuma
// 2,18,John
// """
csv.readCsv[List, Person](rfc)
// res1: List[ReadResult[Person]] = List(
// Right(value = Person(id = 0, name = "Nicolas", age = 38)),
// Right(value = Person(id = 1, name = "Kazuma", age = 1)),
// Right(value = Person(id = 2, name = "John", age = 18))
// )