背景
CMPでデスクトップアプリを作るとなったときに、CSVを読み込む必要が出てきたのでどうやるんだろう…と思って調べたところ、Serializationを使ってCSVをパースできるKotlinx-Serialization-Csvなるものがあったので、備忘録として使い方を残しておきます
Kotlinx Serialization Csvについて
詳細はわからないのですが、SerializationのCsv版のようです
CsvとSerializableの双方向変換ができるようなので、デスクトップアプリとかでは便利なライブラリなのですが、まだドキュメントもなくほとんど情報がないようです(なにか知っている方いましたらコメントでも共有していただけるとありがたいです)
また、jvmでしか書かれていないのでiOS等では動作はしませんので、Windowsデスクトップアプリで使うのが一番よくあるユースケースかなと思われます
使い方
ドキュメントは無いようなので自分の解釈含んだ解説になります
こちらのファイルを見ると生成したCsv
クラスを元にdecode(Csv→Serializable)
,encode(Serializable→Csv)
出来るようです
@OptIn(ExperimentalSerializationApi::class)
fun main() {
// Csvクラスを生成
val csv = Csv { hasHeaderRecord = true }
val records = listOf(
Person("Neo", "Thomas A. Anderson", Appearance(Gender.MALE, 37, 1.86)),
Person("Trinity", null, Appearance(Gender.FEMALE, null, 1.74))
)
// エンコード
val serialized = csv.encodeToString(ListSerializer(Person.serializer()), records)
println(serialized)
}
また、機能は以下になるようです
関数名 | 機能 |
---|---|
encodeToString |
SerializableからCSVの文字列を出力 |
encodeTo |
SerializableからAppendable形式で出力(ファイル出力等に用いる) |
decodeToString |
CSV文字列(String)からSerializableを生成 |
decodeTo |
CSVファイルのReaderからSerializableを生成 |
単純にcsvファイルを読み込むならばReaderを用意して渡してあげることでSerializable化することができます
以下は例(CSVがShift-JISかつMemberというSerializableクラスがある場合)
val path = member.csv
FileInputStream(path).use { fis ->
InputStreamReader(fis, "Shift_JIS").use { isr ->
val members = csv.decodeFrom(
ListSerializer(Member.serializer()),
isr,
)
println("$members")
}
}
また、Csvクラスにはいろんなオプションがあるので用途に応じて使う形になりそうです(以下適当に翻訳)
オプション | デフォルト値 | 説明 |
---|---|---|
delimiter | , | 列間の区切り文字。 |
recordSeparator | \n | レコード区切り文字。 |
quoteChar | " | 列の値を引用するために使用される引用文字。 |
quoteMode | MINIMAL | 列の値を引用するかどうかを決定するために使用される引用モード。 • ALL:すべてのフィールドを引用します。 • ALL_NON_NULL:すべての非nullフィールドと特殊文字を含むフィールドを引用します。 • ALL_NON_NUMERIC:すべての非数値フィールドと特殊文字を含むフィールドを引用します。 • MINIMAL:特殊文字を含むフィールドを引用します。 • NONE:フィールドを引用しません(CsvConfiguration.escapeCharの設定が必要です)。 |
escapeChar | null | 列の値内の予約文字をエスケープするために使用される文字。 |
nullString | 空文字列 | null値を識別するための値。 |
ignoreEmptyLines | true | 解析中に空行を無視します。 |
hasHeaderRecord | false | 最初の行をヘッダーレコードとして扱います。 |
headerSeparator | . | 階層的なヘッダー名を区切るために使用される文字。 |
ignoreUnknownColumns | false | 不明な列を無視します(hasHeaderRecordが有効な場合のみ効果があります)。 |
hasTrailingDelimiter | false | 末尾のレコードが区切り文字で終わる場合。 |
テンプレートとしてDefaultとRfc4180が用意されているようなのでこちらで間に合うなら使うと良いかも
sealed class Csv(val config: CsvConfig) : StringFormat {
...
/**
* Standard Comma Separated Value format.
* Settings are:
* CsvConfig. delimiter = ','
* CsvConfig. quoteChar = '"'
* CsvConfig. recordSeparator = "\n"
* CsvConfig. ignoreEmptyLines = true
*/
companion object Default : Csv(CsvConfig.Default) {
/**
* [RFC 4180](http://tools.ietf.org/html/rfc4180) *Comma Separated Value* format.
*
* Settings are:
* - [CsvConfig.delimiter] = `','`
* - [CsvConfig.quoteChar] = `'"'`
* - [CsvConfig.recordSeparator] = `"\r\n"`
* - [CsvConfig.ignoreEmptyLines] = `false`
*/
val Rfc4180: Csv
get() = Impl(CsvConfig.Rfc4180)
}
総括
ドキュメントが無いとはいえ、Serializerが使えて簡単に変換出来るのは便利なのでWindowsアプリでCSV使いたい!って方は知っておくと良いかも