Skip to content

Examples

This page demonstrates common use cases for kotlin-dsv.

Streaming Large Files

Use encodeToSink and decodeFromSource to work with streams instead of loading everything into memory:

// Generate a sequence of products (could be from a database cursor, etc.)
val productSequence =
  generateSequence(1) { if (it < 100) it + 1 else null }
    .map { Product(it, "Product $it", it * 9.99) }

// Write to a buffer - elements are serialized as the sequence is iterated
val buffer = Buffer()
Csv.encodeToSink(productSequence, buffer)

// Read from the buffer - elements are deserialized as the sequence is consumed
val decodedSequence = Csv.decodeFromSource<Product>(buffer)
decodedSequence.forEach { product ->
  // Process product...
}

This is particularly useful when working with large files:

// Write to file
SystemFileSystem.sink(Path("data.csv")).buffered().use { sink ->
  Csv.encodeToSink(myData, sink)
}

// Read from file
val data =
  SystemFileSystem.source(Path("data.csv")).buffered().use { source ->
    Csv.decodeFromSource<MyData>(source).forEach { item ->
      // Process item...
    }
  }

TSV Format

Work with tab-separated values using the pre-configured Tsv format:

// TSV (tab-separated values) format
val tsv = Tsv.encodeToString(records)
val decoded = Tsv.decodeFromString<Record>(tsv)

Custom Delimiters

Create a custom format with any delimiter:

// Custom delimiter (pipe-separated values)
val format = DsvFormat(scheme = DsvScheme(delimiter = '|'))
val psv = format.encodeToString(items)
val decoded = format.decodeFromString<Data>(psv)

You can also customize the quote character and line endings:

val format =
  DsvFormat(
    scheme =
      DsvScheme(
        delimiter = ';',
        quote = '\'',
        writeCrlf = false, // Use Unix-style line endings
      )
  )

Naming Strategies

Transform property names to match different naming conventions:

@Serializable
data class User(val firstName: String, val lastName: String, val emailAddress: String)

val format = DsvFormat(Csv.scheme, namingStrategy = DsvNamingStrategy.SnakeCase)

val users =
  listOf(User("Amara", "Okafor", "amara@example.com"), User("Chen", "Wei", "chen@example.com"))

val csv = format.encodeToString(users)
// CSV will have headers: first_name,last_name,email_address

val decoded = format.decodeFromString<User>(csv)

Available strategies:

You can also implement custom strategies by extending DsvNamingStrategy.

Handling Missing or Extra Columns

Handle CSVs with incomplete or extra columns:

val format =
  DsvFormat(scheme = Csv.scheme, treatMissingColumnsAsNull = true, ignoreUnknownKeys = true)

// CSV has extra column 'extra' and missing column 'description'
val csv =
  """
  id,name,extra
  1,Item A,ignored
  2,Item B,also ignored
  """
    .trimIndent()

val decoded = format.decodeFromString<PartialData>(csv)
// Missing 'description' column is treated as null
// Extra 'extra' column is ignored

Enum Serialization

Serialize enums by name or ordinal:

@Serializable
enum class Status {
  ACTIVE,
  INACTIVE,
  PENDING,
}

@Serializable data class Item(val id: Int, val status: Status)

val items = listOf(Item(1, Status.ACTIVE), Item(2, Status.PENDING))

// By name (default)
val csvByName = Csv.encodeToString(items)
// Output: id,status\n1,ACTIVE\n2,PENDING

// By ordinal
val formatByOrdinal = DsvFormat(scheme = Csv.scheme, writeEnumsByName = false)
val csvByOrdinal = formatByOrdinal.encodeToString(items)
// Output: id,status\n1,0\n2,2