Skip to content

Commit 32763e5

Browse files
authored
Merge pull request #13 from brudaswen/featue/use-CsvBuilder
Csv configuration via builder pattern
2 parents 6c508fe + 00fb422 commit 32763e5

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

47 files changed

+957
-892
lines changed

CHANGELOG.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,24 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
66

77
## [Unreleased]
88

9+
## [2.0.0] - 2020-11-08
10+
### Added
11+
- `Csv {}` builder function to configure Csv instance.
12+
13+
### Changed
14+
- Use Unix newline (`\n`) as default `recordSeparator` (use `Csv { recordSeparator = "\r\n" }` or
15+
`Csv.Rfc4180` for old behavior).
16+
- Using `QuoteMode.NONE` requires `escapeChar` to be set manually (use
17+
`Csv { quoteMode = QuoteMode.NONE ; escapeChar = '\\' }` for old behavior).
18+
- Last line in CSV is *always* ignored when empty.
19+
- Throws `SerializationException` instead of `IllegalStateException` in case of error.
20+
21+
### Removed
22+
- Removed `CsvConfiguration` (use `Csv {}` builder function instead).
23+
- Removed `Csv.default` (use `Csv { recordSeparator = "\r\n" }` instead).
24+
- Removed `Csv.rfc4180` (use `Csv.Rfc4180` instead).
25+
- Removed `Csv.excel` (use `Csv.Rfc4180` instead).
26+
927
## [1.1.0] - 2020-11-08
1028
### Added
1129
- Support `ignoreUnknownColumns`.

README.md

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,6 @@ import kotlinx.serialization.ExperimentalSerializationApi
3232
import kotlinx.serialization.Serializable
3333
import kotlinx.serialization.builtins.ListSerializer
3434
import kotlinx.serialization.csv.Csv
35-
import kotlinx.serialization.csv.CsvConfiguration
3635

3736
@Serializable
3837
data class Person(val nickname: String, val name: String?, val appearance: Appearance)
@@ -45,7 +44,7 @@ enum class Gender { MALE, FEMALE }
4544

4645
@OptIn(ExperimentalSerializationApi::class)
4746
fun main() {
48-
val csv = Csv(CsvConfiguration(hasHeaderRecord = true))
47+
val csv = Csv { hasHeaderRecord = true }
4948

5049
val records = listOf(
5150
Person("Neo", "Thomas A. Anderson", Appearance(Gender.MALE, 37, 1.86)),
@@ -61,7 +60,7 @@ fun main() {
6160
nickname,appearance.gender,appearance.height,appearance.age,name
6261
Neo,MALE,1.86,37,Thomas A. Anderson
6362
Trinity,FEMALE,1.74,,
64-
""".trimIndent().replace("\n", "\r\n")
63+
""".trimIndent()
6564
val parsed = csv.decodeFromString(ListSerializer(Person.serializer()), input)
6665
println(parsed)
6766
// [
@@ -70,26 +69,25 @@ fun main() {
7069
// ]
7170
}
7271
```
73-
7472
### Pre-defined CSV formats
75-
The library comes with multiple pre-defined formats that can be used out of the box.
73+
The library comes with multiple pre-defined Csv formats that can be used out of the box.
7674

7775
| Config | Description |
7876
|--- |--- |
79-
| `default` | Standard Comma Separated Value format, as for `rfc4180` but allowing empty lines. *Format is unstable and may change in upcoming versions.* |
80-
| `rfc4180` | Comma separated format as defined by [RFC 4180](http://tools.ietf.org/html/rfc4180). |
81-
| `excel` | Excel file format (using a comma as the value delimiter). |
77+
| `Csv.Default` | Standard Comma Separated Value format, as for `Rfc4180` but using Unix newline (`\n`) as record separator and ignoring empty lines. *Format is unstable and may change in upcoming versions.* |
78+
| `Csv.Rfc4180` | Comma separated format as defined by [RFC 4180](http://tools.ietf.org/html/rfc4180). |
8279

8380
### Configuration
84-
CSV serialization and parsing options can be changed by providing a custom `CsvConfiguration`.
81+
CSV serialization and parsing options can be changed by configuring the `Csv` instance during
82+
initialization via the `Csv { }` builder function.
8583

8684
| Option | Default Value | Description |
8785
|--- |--- | --- |
8886
| `delimiter` | `,` | The delimiter character between columns. |
89-
| `recordSeparator` | `\r\n` | The record separator. |
87+
| `recordSeparator` | `\n` | The record separator. |
9088
| `quoteChar` | `"` | The quote character used to quote column values. |
9189
| `quoteMode` | `MINIMAL` | The quote mode used to decide if a column value should get quoted.<ul><li>`ALL`: Quotes *all* fields.</li><li>`ALL_NON_NULL`: Quotes all *non-null fields* and *fields which contain special characters*.</li><li>`ALL_NON_NUMERIC`: Quotes all *non-numeric fields* and *fields which contain special characters*.</li><li>`MINIMAL`: Quotes *fields which contain special characters*.</li><li>`NONE`: *Never* quotes fields (requires `CsvConfiguration.escapeChar` to be set).</li></ul> |
92-
| `escapeChar` | `null` (`\\` for `QuoteMode.NONE`) | The escape character used to escape reserved characters in a column value. |
90+
| `escapeChar` | `null` | The escape character used to escape reserved characters in a column value. |
9391
| `nullString` | *empty string* | The value to identify `null` values. |
9492
| `ignoreEmptyLines` | `true` | Ignore empty lines during parsing. |
9593
| `hasHeaderRecord` | `false` | First line is header record. |

application/src/main/kotlin/de/brudaswen/kotlinx/serialization/example/Example.kt

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import kotlinx.serialization.ExperimentalSerializationApi
44
import kotlinx.serialization.Serializable
55
import kotlinx.serialization.builtins.ListSerializer
66
import kotlinx.serialization.csv.Csv
7-
import kotlinx.serialization.csv.CsvConfiguration
87

98
@Serializable
109
data class Person(val nickname: String, val name: String?, val appearance: Appearance)
@@ -17,7 +16,7 @@ enum class Gender { MALE, FEMALE }
1716

1817
@OptIn(ExperimentalSerializationApi::class)
1918
fun main() {
20-
val csv = Csv(CsvConfiguration(hasHeaderRecord = true))
19+
val csv = Csv { hasHeaderRecord = true }
2120

2221
val records = listOf(
2322
Person("Neo", "Thomas A. Anderson", Appearance(Gender.MALE, 37, 1.86)),
@@ -33,7 +32,7 @@ fun main() {
3332
nickname,appearance.gender,appearance.height,appearance.age,name
3433
Neo,MALE,1.86,37,Thomas A. Anderson
3534
Trinity,FEMALE,1.74,,
36-
""".trimIndent().replace("\n", "\r\n")
35+
""".trimIndent()
3736
val parsed = csv.decodeFromString(ListSerializer(Person.serializer()), input)
3837
println(parsed)
3938
// [

gradle.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
version=1.1.1-SNAPSHOT
1+
version=2.0.0-SNAPSHOT
22
kotlin.code.style=official
33

44
# Disable generation of metadata sha256/sha512 checksum
Lines changed: 42 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,30 @@
11
package kotlinx.serialization.csv
22

33
import kotlinx.serialization.*
4-
import kotlinx.serialization.csv.CsvConfiguration.Companion.rfc4180
4+
import kotlinx.serialization.csv.config.CsvBuilder
5+
import kotlinx.serialization.csv.config.CsvConfig
56
import kotlinx.serialization.csv.decode.CsvReader
67
import kotlinx.serialization.csv.decode.RootCsvDecoder
78
import kotlinx.serialization.csv.decode.StringSource
89
import kotlinx.serialization.csv.encode.RootCsvEncoder
9-
import kotlinx.serialization.modules.EmptySerializersModule
1010
import kotlinx.serialization.modules.SerializersModule
1111

1212
/**
1313
* The main entry point to work with CSV serialization.
1414
*
1515
* It is typically used by constructing an application-specific instance, with configured CSV-specific behaviour
16-
* ([configuration] parameter) and, if necessary, registered
17-
* custom serializers (in [SerializersModule] provided by [context] parameter).
16+
* and, if necessary, registered in [SerializersModule] custom serializers.
17+
* `Csv` instance can be configured in its `Csv {}` factory function using [CsvBuilder].
18+
* For demonstration purposes or trivial usages, Csv [companion][Csv.Default] can be used instead.
1819
*
1920
* Then constructed instance can be used either as regular [SerialFormat] or [StringFormat].
20-
*
21-
* @param configuration CSV settings used during parsing/serialization.
22-
* @param serializersModule Serialization module settings (e.g. custom serializers).
2321
*/
2422
@ExperimentalSerializationApi
25-
class Csv(
26-
internal val configuration: CsvConfiguration,
27-
override val serializersModule: SerializersModule = EmptySerializersModule
28-
) : SerialFormat, StringFormat {
23+
sealed class Csv(internal val config: CsvConfig) : SerialFormat, StringFormat {
24+
25+
override val serializersModule: SerializersModule
26+
get() = config.serializersModule
27+
2928
/**
3029
* Serialize [value] into CSV record(s).
3130
*
@@ -45,60 +44,51 @@ class Csv(
4544
* @param string The CSV string to parse.
4645
*/
4746
override fun <T> decodeFromString(deserializer: DeserializationStrategy<T>, string: String): T {
48-
val reader = CsvReader(StringSource(string), configuration)
47+
val reader = CsvReader(StringSource(string), config)
4948
val input = RootCsvDecoder(this, reader)
5049
val result = input.decodeSerializableValue(deserializer)
5150

5251
require(reader.isDone) { "Reader has not consumed the whole input: $reader" }
5352
return result
5453
}
5554

56-
companion object : StringFormat {
55+
internal class Impl(config: CsvConfig) : Csv(config)
5756

58-
/**
59-
* Standard Comma Separated Value format, as for [rfc4180] but allowing empty lines.
60-
*
61-
* Settings are:
62-
* - [CsvConfiguration.delimiter] = `','`
63-
* - [CsvConfiguration.quoteChar] = `'"'`
64-
* - [CsvConfiguration.recordSeparator] = `"\r\n"`
65-
* - [CsvConfiguration.ignoreEmptyLines] = `true`
66-
*/
67-
val default = Csv(CsvConfiguration.default)
57+
/**
58+
* Standard *Comma Separated Value* format.
59+
*
60+
* Settings are:
61+
* - [CsvConfig.delimiter] = `','`
62+
* - [CsvConfig.quoteChar] = `'"'`
63+
* - [CsvConfig.recordSeparator] = `"\n"`
64+
* - [CsvConfig.ignoreEmptyLines] = `true`
65+
*/
66+
companion object Default : Csv(CsvConfig.Default) {
6867

6968
/**
70-
* Comma separated format as defined by [RFC 4180](http://tools.ietf.org/html/rfc4180).
69+
* [RFC 4180](http://tools.ietf.org/html/rfc4180) *Comma Separated Value* format.
7170
*
7271
* Settings are:
73-
* - [CsvConfiguration.delimiter] = `','`
74-
* - [CsvConfiguration.quoteChar] = `'"'`
75-
* - [CsvConfiguration.recordSeparator] = `"\r\n"`
76-
* - [CsvConfiguration.ignoreEmptyLines] = `false`
72+
* - [CsvConfig.delimiter] = `','`
73+
* - [CsvConfig.quoteChar] = `'"'`
74+
* - [CsvConfig.recordSeparator] = `"\r\n"`
75+
* - [CsvConfig.ignoreEmptyLines] = `false`
7776
*/
78-
val rfc4180 = Csv(CsvConfiguration.rfc4180)
79-
80-
override val serializersModule: SerializersModule
81-
get() = default.serializersModule
82-
83-
/**
84-
* Serialize [value] into CSV record(s) using [CsvConfiguration.default].
85-
*
86-
* @param serializer The serializer used to serialize the given object.
87-
* @param value The [Serializable] object.
88-
*/
89-
override fun <T> encodeToString(serializer: SerializationStrategy<T>, value: T): String =
90-
default.encodeToString(serializer, value)
77+
val Rfc4180: Csv
78+
get() = Impl(CsvConfig.Rfc4180)
79+
}
80+
}
9181

92-
/**
93-
* Parse CSV [string] into [Serializable] object using [CsvConfiguration.default].
94-
*
95-
* @param deserializer The deserializer used to parse the given CSV string.
96-
* @param string The CSV string to parse.
97-
*/
98-
override fun <T> decodeFromString(
99-
deserializer: DeserializationStrategy<T>,
100-
string: String
101-
): T =
102-
default.decodeFromString(deserializer, string)
82+
/**
83+
* Creates an instance of [Csv] configured from the optionally given [Csv instance][from] and
84+
* adjusted with [action].
85+
*/
86+
@ExperimentalSerializationApi
87+
@Suppress("FunctionName")
88+
fun Csv(from: Csv = Csv.Default, action: CsvBuilder.() -> Unit): Csv {
89+
val conf = CsvBuilder(from.config).run {
90+
action()
91+
build()
10392
}
93+
return Csv.Impl(conf)
10494
}

library/src/main/kotlin/kotlinx/serialization/csv/CsvConfiguration.kt

Lines changed: 0 additions & 132 deletions
This file was deleted.

0 commit comments

Comments
 (0)