Kotlin Serialization ガイド　第6章 代替フォーマットとカスタムフォーマット(実験)

#はじめに
　[Kotlin Serialization ガイドの目次](https://qiita.com/guijiu/items/097e858e4930b6d7e6c0)に基づいて「[Kotlin Serialization guide](https://github.com/Kotlin/kotlinx.serialization/blob/master/docs/serialization-guide.md#kotlin-serialization-guide)」を訳しています。
　なお、翻訳には[DeepL](https://www.deepl.com/translator)の力を９９％借りています。
　もし、一緒に「[Kotlin Serialization ガイド](https://github.com/Kotlin/kotlinx.serialization/blob/master/docs/serialization-guide.md#kotlin-serialization-guide)」の翻訳をして下さる方がいらっしゃいましたら、コメント欄などから連絡をください。

# 第６章　[代替フォーマットとカスタムフォーマット(実験)](https://github.com/Kotlin/kotlinx.serialization/blob/master/docs/formats.md)

<!--- TEST_NAME FormatsTest -->

　これは [Kotlin Serialization Guide](https://github.com/Kotlin/kotlinx.serialization/blob/master/docs/serialization-guide.md#kotlin-serialization-guide) の第6章です。JSONを超えて、代替フォーマットやカスタムフォーマットを網羅しています。安定している JSON とは異なり、これらは現在 Kotlin Serialization の実験的な機能です。


**目次**

* [CBOR（実験）](#cbor実験)
  * [未知のキーを無視](#未知のキーを無視)
  * [バイト配列とCBORデータ型](#バイト配列とcborデータ型)
* [プロトバッファ (実験)](#プロトバッファ-実験)
  * [フィールド番号](#フィールド番号)
  * [整数型](#整数型)
  * [繰り返しのフィールドとしてリスト](#繰り返しのフィールドとしてリスト)
* [プロパティ（実験）](#プロパティ実験)
* [カスタムフォーマット (実験)](#カスタムフォーマット-実験)
  * [基本的なエンコーダ](#基本的なエンコーダ)
  * [基本的なデコーダ](#基本的なデコーダ)
  * [逐次復号化](#逐次復号化)
  * [コレクションサポートの追加](#コレクションサポートの追加)
  * [nullサポートの追加](#nullサポートの追加)
  * [効率的なバイナリ形式](#効率的なバイナリ形式)
  * [フォーマット固有の型](#フォーマット固有の型)

<!--- END -->

## CBOR（実験）

　[CBOR][RFC 7049]はJSONの標準コンパクトバイナリエンコーディングの1つなので、[JSONの機能](https://qiita.com/guijiu/items/9bcbe7379026d88978da#%E7%AC%AC%EF%BC%95%E7%AB%A0json%E3%81%AE%E6%A9%9F%E8%83%BD%E5%8E%9F%E6%96%87)のサブセットをサポートしており、一般的には使用中のJSONと非常によく似ていますが、バイナリデータを生成します。

> CBOR のサポートは (実験的に) 別の `org.jetbrains.kotlinx:kotlinx-serialization-cbor:<version>` モジュールで利用可能です。


　[Cbor]クラスには[Cbor.encodeToByteArray]と[Cbor.decodeFromByteArray]関数があります。
ここでは、[JSONエンコーディング](https://qiita.com/guijiu/items/4defa057b800a2348607#json-%E3%82%A8%E3%83%B3%E3%82%B3%E3%83%BC%E3%83%87%E3%82%A3%E3%83%B3%E3%82%B0)の基本的な例を見ながら、CBORを使ってエンコードしてみましょう。


<!--- INCLUDE
import kotlinx.serialization.*
import kotlinx.serialization.cbor.*

fun ByteArray.toAsciiHexString() = joinToString("") {
    if (it in 32..127) it.toChar().toString() else
        "{${it.toUByte().toString(16).padStart(2, '0').toUpperCase()}}"
}
-->

```kotlin    
@Serializable
data class Project(val name: String, val language: String)

fun main() {
    val data = Project("kotlinx.serialization", "Kotlin") 
    val bytes = Cbor.encodeToByteArray(data)   
    println(bytes.toAsciiHexString())
    val obj = Cbor.decodeFromByteArray<Project>(bytes)
    println(obj)
}
```                                  

> 完全なコードは[こちら](https://github.com/Kotlin/kotlinx.serialization/blob/master/guide/example/example-formats-01.kt)から取得できます。

　非ASCIIデータを16進数で書き出し、フィルタリングされたASCII表現を出力しています。


```text 
{BF}dnameukotlinx.serializationhlanguagefKotlin{FF}
Project(name=kotlinx.serialization, language=Kotlin)
```

<!--- TEST -->

　[CBOR 16進数表記](http://cbor.me/)では、以下のように出力されます。

```text
BF                                      # map(*)
   64                                   # text(4)
      6E616D65                          # "name"
   75                                   # text(21)
      6B6F746C696E782E73657269616C697A6174696F6E # "kotlinx.serialization"
   68                                   # text(8)
      6C616E6775616765                  # "language"
   66                                   # text(6)
      4B6F746C696E                      # "Kotlin"
   FF                                   # primitive(*)
```

> 注意: フォーマットとしての CBOR は JSON とは異なり、非自明なキーを持つマップをサポートしています (JSON の回避策については [構造化されたマップキーの使用許可](https://qiita.com/guijiu/items/9bcbe7379026d88978da#%E6%A7%8B%E9%80%A0%E5%8C%96%E3%81%95%E3%82%8C%E3%81%9F%E3%83%9E%E3%83%83%E3%83%97%E3%82%AD%E3%83%BC%E3%81%AE%E4%BD%BF%E7%94%A8%E8%A8%B1%E5%8F%AF) のセクションを参照してください)。

### 未知のキーを無視

　CBORフォーマットは、デバイスのAPI進化の一環として新しいプロパティが追加される可能性がある[IoT]デバイスとの通信によく使用される。既定では、デシリアライズ中に未知のキーが検出されるとエラーが発生します。この動作は、[ignoreUnknownKeys][CborBuilder.ignoreUnknownKeys]プロパティで設定できます。


<!--- INCLUDE
import kotlinx.serialization.*
import kotlinx.serialization.cbor.*
-->

```kotlin
val format = Cbor { ignoreUnknownKeys = true }

@Serializable
data class Project(val name: String)

fun main() {
    val data = format.decodeFromHexString<Project>(
        "bf646e616d65756b6f746c696e782e73657269616c697a6174696f6e686c616e6775616765664b6f746c696eff"
    )
    println(data)
}
```

> 完全なコードは[こちら](https://github.com/Kotlin/kotlinx.serialization/blob/master/guide/example/example-formats-02.kt)から取得できます。

　`Project` が `language` プロパティを欠いているにもかかわらず、オブジェクトをデコードします。

```text
Project(name=kotlinx.serialization)
```

<!--- TEST -->

　[CBOR 16進数表記](http://cbor.me/)では、入力は以下のようになります。

```
BF                                      # map(*)
   64                                   # text(4)
      6E616D65                          # "name"
   75                                   # text(21)
      6B6F746C696E782E73657269616C697A6174696F6E # "kotlinx.serialization"
   68                                   # text(8)
      6C616E6775616765                  # "language"
   66                                   # text(6)
      4B6F746C696E                      # "Kotlin"
   FF                                   # primitive(*)
```

### バイト配列とCBORデータ型

　[RFC 7049 Major Types]のセクションでは、CBORは以下のデータタイプをサポートしています。

- メジャー型 0：符号なし整数
- メジャータイプ1：負の整数
- 主要なタイプ 2: バイト文字列**。
- メジャータイプ3：テキスト文字列
- 主な型4：データ項目の配列**。
- メジャータイプ5：データ項目のペアのマップ
- メジャータイプ6：他のメジャータイプの任意の意味タグ付け
- メジャー型7：浮動小数点数と内容のない単純なデータ型、および「ブレーク」の停止コード

　デフォルトでは、Kotlin の `ByteArray` インスタンスは **メジャータイプ 4** としてエンコードされます。メジャータイプ2**が必要な場合は、[`@ByteString`](https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-cbor/kotlinx-serialization-cbor/kotlinx.serialization.cbor/-byte-string/index.html)アノテーションを使用することができます。

<!--- INCLUDE
import kotlinx.serialization.*
import kotlinx.serialization.cbor.*

fun ByteArray.toAsciiHexString() = joinToString("") {
    if (it in 32..127) it.toChar().toString() else
        "{${it.toUByte().toString(16).padStart(2, '0').toUpperCase()}}"
}
-->

```kotlin
@Serializable
data class Data(
    @ByteString
    val type2: ByteArray, // CBOR Major type 2
    val type4: ByteArray  // CBOR Major type 4
)        

fun main() {
    val data = Data(byteArrayOf(1, 2, 3, 4), byteArrayOf(5, 6, 7, 8)) 
    val bytes = Cbor.encodeToByteArray(data)   
    println(bytes.toAsciiHexString())
    val obj = Cbor.decodeFromByteArray<Data>(bytes)
    println(obj)
}
```

> 完全なコードは[こちら](https://github.com/Kotlin/kotlinx.serialization/blob/master/guide/example/example-formats-03.kt)から取得できます。   

　このように、データに先行するCBORバイトは、エンコーディングの種類によって異なります。

```text
{BF}etype2D{01}{02}{03}{04}etype4{9F}{05}{06}{07}{08}{FF}{FF}
Data(type2=[1, 2, 3, 4], type4=[5, 6, 7, 8])
```

<!--- TEST -->

　[CBOR 16進数表記](http://cbor.me/)では、以下のように出力されます。

```
BF               # map(*)
   65            # text(5)
      7479706532 # "type2"
   44            # bytes(4)
      01020304   # "\x01\x02\x03\x04"
   65            # text(5)
      7479706534 # "type4"
   9F            # array(*)
      05         # unsigned(5)
      06         # unsigned(6)
      07         # unsigned(7)
      08         # unsigned(8)
      FF         # primitive(*)
   FF            # primitive(*)
```

## プロトバッファ (実験)

　[Protocol Buffers](https://developers.google.com/protocol-buffers) は言語ニュートラルなバイナリフォーマットで、通常はプロトコルスキーマを定義する別の".proto "ファイルに依存します。名前の代わりに整数をフィールドに割り当てるため、CBORよりもコンパクトです。

> プロトコルバッファのサポートは、(実験的に)別の `org.jetbrains.kotlinx:kotlinx-serialization-protobuf:<version>` モジュールで利用可能です。

　Kotlin Serialization は proto2 セマンティクスを使用しており、すべてのフィールドは明示的に必須または任意である。基本的な例として、[ProtoBuf.encodeToByteArray]と[ProtoBuf.decodeFromByteArray]関数を持つ[ProtoBuf]クラスを使用するように変更します。

<!--- INCLUDE
import kotlinx.serialization.*
import kotlinx.serialization.protobuf.*

fun ByteArray.toAsciiHexString() = joinToString("") {
    if (it in 32..127) it.toChar().toString() else
        "{${it.toUByte().toString(16).padStart(2, '0').toUpperCase()}}"
}
-->

```kotlin    
@Serializable
data class Project(val name: String, val language: String)

fun main() {
    val data = Project("kotlinx.serialization", "Kotlin") 
    val bytes = ProtoBuf.encodeToByteArray(data)   
    println(bytes.toAsciiHexString())
    val obj = ProtoBuf.decodeFromByteArray<Project>(bytes)
    println(obj)
}
```                                  

> 完全なコードは[こちら](https://github.com/Kotlin/kotlinx.serialization/blob/master/guide/example/example-formats-04.kt)から取得できます。

```text 
{0A}{15}kotlinx.serialization{12}{06}Kotlin
Project(name=kotlinx.serialization, language=Kotlin)
```

<!--- TEST -->

[ProtoBuf 16進表記](https://protogen.marcgravell.com/decode)では、以下のように出力されます。

```
Field #1: 0A String Length = 21, Hex = 15, UTF8 = "kotlinx.serialization"
Field #2: 12 String Length = 6, Hex = 06, UTF8 = "Kotlin"
```

### フィールド番号

　デフォルトでは、Kotlin Serialization [ProtoBuf]実装のフィールド番号は自動的に割り当てられますが、これでは時間の経過とともに進化する安定したデータスキーマを定義する機能がありません。これは通常、別の ".proto" ファイルを書くことで実現されます。しかし、Kotlin Serializationでは、別のスキーマファイルを書くことなく、代わりに [ProtoNumber] アノテーションを使ってこの機能を得ることができます。


<!--- INCLUDE
import kotlinx.serialization.*
import kotlinx.serialization.protobuf.*

fun ByteArray.toAsciiHexString() = joinToString("") {
    if (it in 32..127) it.toChar().toString() else
        "{${it.toUByte().toString(16).padStart(2, '0').toUpperCase()}}"
}
-->

```kotlin    
@Serializable
data class Project(
    @ProtoNumber(1)
    val name: String, 
    @ProtoNumber(3)
    val language: String
)

fun main() {
    val data = Project("kotlinx.serialization", "Kotlin") 
    val bytes = ProtoBuf.encodeToByteArray(data)   
    println(bytes.toAsciiHexString())
    val obj = ProtoBuf.decodeFromByteArray<Project>(bytes)
    println(obj)
}
```                                  

> 完全なコードは[こちら](https://github.com/Kotlin/kotlinx.serialization/blob/master/guide/example/example-formats-05.kt)から取得できます。

　出力を見ると、最初のプロパティ `name` の番号は変更されていませんが (デフォルトでは 1 から番号が付けられているため)、`language` プロパティの番号は変更されています。


```text 
{0A}{15}kotlinx.serialization{1A}{06}Kotlin
Project(name=kotlinx.serialization, language=Kotlin)
```    

<!--- TEST -->

[ProtoBuf 16進表記](https://protogen.marcgravell.com/decode)では、以下のように出力されます。

```
Field #1: 0A String Length = 21, Hex = 15, UTF8 = "kotlinx.serialization" (total 21 chars)
Field #3: 1A String Length = 6, Hex = 06, UTF8 = "Kotlin"
```

### 整数型

　プロトコルバッファは、異なる整数範囲に最適化された様々な整数エンコーディングをサポートしています。これらは、[ProtoType]アノテーションと[ProtoIntegerType]列挙を使用して指定されます。次の例では、サポートされている3つのオプションをすべて示しています。

<!--- INCLUDE
import kotlinx.serialization.*
import kotlinx.serialization.protobuf.*

fun ByteArray.toAsciiHexString() = joinToString("") {
    if (it in 32..127) it.toChar().toString() else
        "{${it.toUByte().toString(16).padStart(2, '0').toUpperCase()}}"
}
-->

```kotlin    
@Serializable
class Data(
    @ProtoType(ProtoIntegerType.DEFAULT)
    val a: Int,
    @ProtoType(ProtoIntegerType.SIGNED)
    val b: Int,
    @ProtoType(ProtoIntegerType.FIXED)
    val c: Int
)

fun main() {
    val data = Data(1, -2, 3) 
    println(ProtoBuf.encodeToByteArray(data).toAsciiHexString())
}
```                   

> 完全なコードは[こちら](https://github.com/Kotlin/kotlinx.serialization/blob/master/guide/example/example-formats-06.kt)から取得できます。

* [default][ProtoIntegerType.DEFAULT]は、小さな非負数用に最適化された varint エンコーディング (`intXX`) です。1` の値は、1バイトの `01` でエンコードされる。

* [signed][ProtoIntegerType.SIGNED]は符号付きZigZagエンコーディング(`sintXX`)であり、小さな符号付き整数用に最適化されています。`2` の値は 1 バイトの `03` にエンコードされる。

* [固定][ProtoIntegerType.FIXED]エンコーディング(`fixedXX`)は、常に固定のバイト数を使用します。
  `3` の値は `03 00 00 00` の4バイトでエンコードされる。

> uintXX` および `sfixedXX` プロトコルバッファ型はサポートされていません。   

```text 
{08}{01}{10}{03}{1D}{03}{00}{00}{00}
```          

<!--- TEST -->

[ProtoBuf 16進表記](https://protogen.marcgravell.com/decode)では、以下のように出力されます。

```
Field #1: 08 Varint Value = 1, Hex = 01
Field #2: 10 Varint Value = 3, Hex = 03
Field #3: 1D Fixed32 Value = 3, Hex = 03-00-00-00
```

### 繰り返しのフィールドとしてリスト

　Kotlinのリストやその他のコレクションは、繰り返しフィールドとして表現されます。プロトコルバッファでは、リストが空の場合、対応する番号の要素がストリーム内に存在しません。Kotlin Serializationでは、コレクションやマップ型のプロパティに `emptyList()` のデフォルト値を明示的に指定する必要があります。そうでなければ、空のリストをデシリアライズすることができません。これは、プロトコルバッファでは欠落したフィールドと区別がつきません。 

<!--- INCLUDE
import kotlinx.serialization.*
import kotlinx.serialization.protobuf.*

fun ByteArray.toAsciiHexString() = joinToString("") {
    if (it in 32..127) it.toChar().toString() else
        "{${it.toUByte().toString(16).padStart(2, '0').toUpperCase()}}"
}
-->

```kotlin    
@Serializable
data class Data(
    val a: List<Int> = emptyList(),
    val b: List<Int> = emptyList()
)

fun main() {
    val data = Data(listOf(1, 2, 3), listOf())
    val bytes = ProtoBuf.encodeToByteArray(data)
    println(bytes.toAsciiHexString())
    println(ProtoBuf.decodeFromByteArray<Data>(bytes))
}
```                   

> 完全なコードは[こちら](https://github.com/Kotlin/kotlinx.serialization/blob/master/guide/example/example-formats-07.kt)から取得できます。

```text 
{08}{01}{08}{02}{08}{03}
Data(a=[1, 2, 3], b=[])
```

<!--- TEST -->

> パックされた繰り返しフィールドはサポートされていません。

[ProtoBuf診断モード](https://protogen.marcgravell.com/decode)では、以下のような出力になります。

```
Field #1: 08 Varint Value = 1, Hex = 01
Field #1: 08 Varint Value = 2, Hex = 02
Field #1: 08 Varint Value = 3, Hex = 03
```

## プロパティ（実験）

　Kotlin Serializationは、[Properties] [kotlinx.serialization.properties.Properties]形式の実装を介して、`String`キーを持つフラットマップにクラスをシリアライズすることができます。


> プロパティのサポートは、(実験的に)別の `org.jetbrains.kotlinx:kotlinx-serialization-properties:<version>` モジュールで利用可能です。


<!--- INCLUDE
import kotlinx.serialization.*
import kotlinx.serialization.properties.Properties // todo: remove when no longer needed
import kotlinx.serialization.properties.*
-->

```kotlin    
@Serializable
class Project(val name: String, val owner: User)

@Serializable
class User(val name: String)

fun main() {
    val data = Project("kotlinx.serialization",  User("kotlin"))
    val map = Properties.encodeToMap(data)
    map.forEach { (k, v) -> println("$k = $v") }
}
```      

> 完全なコードは[こちら](https://github.com/Kotlin/kotlinx.serialization/blob/master/guide/example/example-formats-08.kt)から取得できます。

　結果として得られるマップは、入れ子になったオブジェクトのキーを表すドット区切りのキーを持っています。

```text 
name = kotlinx.serialization
owner.name = kotlin
```

<!--- TEST -->

## カスタムフォーマット (実験)

　Kotlin Serialization用のカスタムフォーマットは、[Serializers](https://qiita.com/guijiu/items/be7fa14c49cb7e3006dd#%E7%AC%AC3%E7%AB%A0-%E3%82%B7%E3%83%AA%E3%82%A2%E3%83%A9%E3%82%A4%E3%82%B6-%E5%8E%9F%E6%96%87)の章で使用した[Encoder]と[Decoder]インターフェースの実装を提供しなければなりません。 
　これらはかなり大きなインターフェースです。便利なように、[AbstractEncoder]と[AbstractDecoder]のスケルトン実装が提供されています。[AbstractEncoder]では、ほとんどの `encodeXxx` メソッドはデフォルトで [`encodeValue(value: Any)`] [AbstractEncoder.encodeValue] &mdash; にデリゲートする実装を持っており、基本的な作業形式を取得するために実装しなければならない唯一のメソッドです。

### 基本的なエンコーダ

　まずは、ソースコードに書かれている順にデータをプリミティブな構成オブジェクトの単一のリストにエンコードするという、些細なフォーマットの実装から始めてみましょう。まず、[AbstractEncoder]の`encodeValue`をオーバーライドして、簡単な[Encoder]を実装します。

<!--- INCLUDE
import kotlinx.serialization.*
import kotlinx.serialization.descriptors.*
import kotlinx.serialization.encoding.*
import kotlinx.serialization.modules.*
-->

```kotlin
class ListEncoder : AbstractEncoder() {
    val list = mutableListOf<Any>()

    override val serializersModule: SerializersModule = EmptySerializersModule

    override fun encodeValue(value: Any) {
        list.add(value)
    }
}
```              

　ここで、オブジェクトをエンコードしてリストを返すエンコーダを作成する便利なトップレベル関数を書きます。

```kotlin
fun <T> encodeToList(serializer: SerializationStrategy<T>, value: T): List<Any> {
    val encoder = ListEncoder()
    encoder.encodeSerializableValue(serializer, value)
    return encoder.list
}
```                     

　シリアライザを明示的に渡さなくてもよいように、`encodeToList` 関数の `inline` オーバーロードに `reified` 型パラメータを指定して [serializer] 関数を用いて、実際の型に適した [KSerializer] インスタンスを取得するようにしています。

```kotlin 
inline fun <reified T> encodeToList(value: T) = encodeToList(serializer(), value)
```                  

これでテストできるようになりました。


```kotlin 
@Serializable
data class Project(val name: String, val owner: User, val votes: Int)

@Serializable
data class User(val name: String)

fun main() {
    val data = Project("kotlinx.serialization",  User("kotlin"), 9000)
    println(encodeToList(data))
}
```                                    

> 完全なコードは[こちら](https://github.com/Kotlin/kotlinx.serialization/blob/master/guide/example/example-formats-09.kt)から取得できます。

　その結果、オブジェクトグラフ内のすべてのプリミティブ値を取得し、_serial_順にリストに入れました。


```text
[kotlinx.serialization, kotlin, 9000]
```    

<!--- TEST -->

> それ自体は、シリアライズ可能なオブジェクトツリーに含まれるすべてのデータに対して、ある種のハッシュコードやダイジェストを計算する必要がある場合に便利な機能です。


### 基本的なデコーダ

<!--- INCLUDE 
import kotlinx.serialization.*
import kotlinx.serialization.descriptors.*
import kotlinx.serialization.encoding.*
import kotlinx.serialization.modules.*

class ListEncoder : AbstractEncoder() {
    val list = mutableListOf<Any>()

    override val serializersModule: SerializersModule = EmptySerializersModule

    override fun encodeValue(value: Any) {
        list.add(value)
    }
}

fun <T> encodeToList(serializer: SerializationStrategy<T>, value: T): List<Any> {
    val encoder = ListEncoder()
    encoder.encodeSerializableValue(serializer, value)
    return encoder.list
}

inline fun <reified T> encodeToList(value: T) = encodeToList(serializer(), value)
-->

　デコーダはもっと実体のあるものを実装する必要があります。

* [decodeValue][AbstractDecoder.decodeValue] &mdash; リストの中から次の値を返します。
* [decodeElementIndex][CompositeDecoder.decodeElementIndex] &mdash; デシリアライズされた値の次のインデックスを返します。
  このプリミティブ形式では、デシリアライズは常に順番に行われるので、変数 `elementIndex` にインデックスを記録しておきます。これがどのように使われるかについては、[手書き複合シリアライザ](https://qiita.com/guijiu/items/be7fa14c49cb7e3006dd#%E6%89%8B%E6%9B%B8%E3%81%8D%E8%A4%87%E5%90%88%E3%82%B7%E3%83%AA%E3%82%A2%E3%83%A9%E3%82%A4%E3%82%B6)のセクションを参照してください。
* [beginStructure][Decoder.beginStructure] &mdash; 再帰的にデコードされる各構造体がそれ自身の `elementIndex` 状態を別々に追跡するように、`ListDecoder` の新しいインスタンスを返します。 

```kotlin
class ListDecoder(val list: ArrayDeque<Any>) : AbstractDecoder() {
    private var elementIndex = 0

    override val serializersModule: SerializersModule = EmptySerializersModule

    override fun decodeValue(): Any = list.removeFirst()
    
    override fun decodeElementIndex(descriptor: SerialDescriptor): Int {
        if (elementIndex == descriptor.elementsCount) return CompositeDecoder.DECODE_DONE
        return elementIndex++
    }

    override fun beginStructure(descriptor: SerialDescriptor): CompositeDecoder =
        ListDecoder(list)
}
```

　デコードに便利な機能がいくつかあります。

```kotlin
fun <T> decodeFromList(list: List<Any>, deserializer: DeserializationStrategy<T>): T {
    val decoder = ListDecoder(ArrayDeque(list))
    return decoder.decodeSerializableValue(deserializer)
}

inline fun <reified T> decodeFromList(list: List<Any>): T = decodeFromList(list, serializer())
```

　これだけで、基本的なシリアライズ可能なクラスのエンコードとデコードを開始することができます。

<!--- INCLUDE

@Serializable
data class Project(val name: String, val owner: User, val votes: Int)

@Serializable
data class User(val name: String)
-->

```kotlin    
fun main() {
    val data = Project("kotlinx.serialization",  User("kotlin"), 9000)
    val list = encodeToList(data)
    println(list)
    val obj = decodeFromList<Project>(list)
    println(obj)
}
```

> フルコードは[こちら](https://github.com/Kotlin/kotlinx.serialization/blob/master/guide/example/example-formats-10.kt)取得することができます。

　これで、プリミティブのリストをオブジェクトツリーに戻すことができます。

```text
[kotlinx.serialization, kotlin, 9000]
Project(name=kotlinx.serialization, owner=User(name=kotlin), votes=9000)
```               

<!--- TEST -->

### 逐次復号化

　我々が実装したデコーダは `elementIndex` の状態を追跡して `decodeElementIndex` を実装している。つまり、[手書き複合シリアライザ](https://qiita.com/guijiu/items/be7fa14c49cb7e3006dd#%E6%89%8B%E6%9B%B8%E3%81%8D%E8%A4%87%E5%90%88%E3%82%B7%E3%83%AA%E3%82%A2%E3%83%A9%E3%82%A4%E3%82%B6)の項で書いた単純なシリアライザでも、任意のシリアライザで動作するようになるということです。しかし、このフォーマットは常に要素を順番に格納するので、このブックキーピングは必要なく、デコード性能を損なう。JVM上のすべての自動生成シリアライザは[逐次デコーディングプロトコル(エクスペリメンタル)](https://qiita.com/guijiu/items/be7fa14c49cb7e3006dd#%E9%80%90%E6%AC%A1%E5%BE%A9%E5%8F%B7%E5%8C%96%E3%83%97%E3%83%AD%E3%83%88%E3%82%B3%E3%83%AB%E5%AE%9F%E9%A8%93)をサポートしており、デコーダは[CompositeDecoder.decodeSequentially]関数から`true`を返すことでサポートしていることを示すことができます。

<!--- INCLUDE
import kotlinx.serialization.*
import kotlinx.serialization.descriptors.*
import kotlinx.serialization.encoding.*
import kotlinx.serialization.modules.*

class ListEncoder : AbstractEncoder() {
    val list = mutableListOf<Any>()

    override val serializersModule: SerializersModule = EmptySerializersModule

    override fun encodeValue(value: Any) {
        list.add(value)
    }
}

fun <T> encodeToList(serializer: SerializationStrategy<T>, value: T): List<Any> {
    val encoder = ListEncoder()
    encoder.encodeSerializableValue(serializer, value)
    return encoder.list
}

inline fun <reified T> encodeToList(value: T) = encodeToList(serializer(), value)
-->

```kotlin
class ListDecoder(val list: ArrayDeque<Any>) : AbstractDecoder() {
    private var elementIndex = 0

    override val serializersModule: SerializersModule = EmptySerializersModule

    override fun decodeValue(): Any = list.removeFirst()
    
    override fun decodeElementIndex(descriptor: SerialDescriptor): Int {
        if (elementIndex == descriptor.elementsCount) return CompositeDecoder.DECODE_DONE
        return elementIndex++
    }

    override fun beginStructure(descriptor: SerialDescriptor): CompositeDecoder =
        ListDecoder(list) 

    override fun decodeSequentially(): Boolean = true
}        
```

<!--- INCLUDE

fun <T> decodeFromList(list: List<Any>, deserializer: DeserializationStrategy<T>): T {
    val decoder = ListDecoder(ArrayDeque(list))
    return decoder.decodeSerializableValue(deserializer)
}

inline fun <reified T> decodeFromList(list: List<Any>): T = decodeFromList(list, serializer())

@Serializable
data class Project(val name: String, val owner: User, val votes: Int)

@Serializable
data class User(val name: String)

fun main() {
    val data = Project("kotlinx.serialization",  User("kotlin"), 9000)
    val list = encodeToList(data)
    println(list)
    val obj = decodeFromList<Project>(list)
    println(obj)
}
-->

> 完全なコードは[こちら](https://github.com/Kotlin/kotlinx.serialization/blob/master/guide/example/example-formats-11.kt)から取得できます。

<!--- TEST 
[kotlinx.serialization, kotlin, 9000]
Project(name=kotlinx.serialization, owner=User(name=kotlin), votes=9000)
-->   

### コレクションサポートの追加

　この基本的なフォーマットでは、今のところコレクションを適切に表現することはできません。In はエンコードしますが、コレクションの中に何個の要素があるのか、どこで終わるのかを記録していないので、正しくデコードすることができません。まず、[Encoder.beginCollection] 関数を実装して、エンコーダにコレクションの適切なサポートを追加してみましょう。関数 `beginCollection` はコレクションのサイズをパラメータとして受け取るので、それをエンコードして結果に追加します。私たちのエンコーダの実装は状態を保持しないので、`beginCollection` 関数から `this` を返すだけです。

<!--- INCLUDE 
import kotlinx.serialization.*
import kotlinx.serialization.descriptors.*
import kotlinx.serialization.encoding.*
import kotlinx.serialization.modules.*
-->

```kotlin
class ListEncoder : AbstractEncoder() {
    val list = mutableListOf<Any>()

    override val serializersModule: SerializersModule = EmptySerializersModule

    override fun encodeValue(value: Any) {
        list.add(value)
    }                               

    override fun beginCollection(descriptor: SerialDescriptor, collectionSize: Int): CompositeEncoder {
        encodeInt(collectionSize)
        return this
    }                                                
}
```

<!--- INCLUDE

fun <T> encodeToList(serializer: SerializationStrategy<T>, value: T): List<Any> {
    val encoder = ListEncoder()
    encoder.encodeSerializableValue(serializer, value)
    return encoder.list
}

inline fun <reified T> encodeToList(value: T) = encodeToList(serializer(), value)
-->

　私たちの場合、デコーダは、以前のコードに加えて [CompositeDecoder.decodeCollectionSize] 関数を実装する必要があります。

> コレクションサイズをあらかじめ格納するフォーマットは、`decodeSequentially`から`true`を返さなければならない。

```kotlin
class ListDecoder(val list: ArrayDeque<Any>, var elementsCount: Int = 0) : AbstractDecoder() {
    private var elementIndex = 0

    override val serializersModule: SerializersModule = EmptySerializersModule

    override fun decodeValue(): Any = list.removeFirst()

    override fun decodeElementIndex(descriptor: SerialDescriptor): Int {
        if (elementIndex == elementsCount) return CompositeDecoder.DECODE_DONE
        return elementIndex++
    }

    override fun beginStructure(descriptor: SerialDescriptor): CompositeDecoder =
        ListDecoder(list, descriptor.elementsCount)

    override fun decodeSequentially(): Boolean = true

    override fun decodeCollectionSize(descriptor: SerialDescriptor): Int =
        decodeInt().also { elementsCount = it }
}
```

<!--- INCLUDE

fun <T> decodeFromList(list: List<Any>, deserializer: DeserializationStrategy<T>): T {
    val decoder = ListDecoder(ArrayDeque(list))
    return decoder.decodeSerializableValue(deserializer)
}

inline fun <reified T> decodeFromList(list: List<Any>): T = decodeFromList(list, serializer())
-->

　コレクションやMapをサポートするために必要なのはそれだけです。

```kotlin     
@Serializable
data class Project(val name: String, val owners: List<User>, val votes: Int)

@Serializable
data class User(val name: String)

fun main() {
    val data = Project("kotlinx.serialization",  listOf(User("kotlin"), User("jetbrains")), 9000)
    val list = encodeToList(data)
    println(list)
    val obj = decodeFromList<Project>(list)
    println(obj)
}
```

> フルコードは[こちら](https://github.com/Kotlin/kotlinx.serialization/blob/master/guide/example/example-formats-12.kt)から取得することができます。

　デコーダに停止場所を知らせるために、結果に追加されたリストのサイズを見ることができます。

```text
[kotlinx.serialization, 2, kotlin, jetbrains, 9000]
Project(name=kotlinx.serialization, owners=[User(name=kotlin), User(name=jetbrains)], votes=9000)
```                      

<!--- TEST -->

### nullサポートの追加

　私たちのトリビアルフォーマットは今のところ `null` 値をサポートしていません。null可能な型には、何らかの "nullインジケータ "を追加する必要があります。

<!--- INCLUDE 
import kotlinx.serialization.*
import kotlinx.serialization.descriptors.*
import kotlinx.serialization.encoding.*
import kotlinx.serialization.modules.*

class ListEncoder : AbstractEncoder() {
    val list = mutableListOf<Any>()

    override val serializersModule: SerializersModule = EmptySerializersModule

    override fun encodeValue(value: Any) {
        list.add(value)
    }                               

    override fun beginCollection(descriptor: SerialDescriptor, collectionSize: Int): CompositeEncoder {
        encodeInt(collectionSize)
        return this
    }                                                
-->

　エンコーダの実装では、[Encoder.encodeNull]と[Encoder.encodeNotNullMark]をオーバーライドしています。

```kotlin            
    override fun encodeNull() = encodeValue("NULL")
    override fun encodeNotNullMark() = encodeValue("!!")
```

<!--- INCLUDE
}

fun <T> encodeToList(serializer: SerializationStrategy<T>, value: T): List<Any> {
    val encoder = ListEncoder()
    encoder.encodeSerializableValue(serializer, value)
    return encoder.list
}

inline fun <reified T> encodeToList(value: T) = encodeToList(serializer(), value)

class ListDecoder(val list: ArrayDeque<Any>, var elementsCount: Int = 0) : AbstractDecoder() {
    private var elementIndex = 0
    
    override val serializersModule: SerializersModule = EmptySerializersModule

    override fun decodeValue(): Any = list.removeFirst()

    override fun decodeElementIndex(descriptor: SerialDescriptor): Int {
        if (elementIndex == elementsCount) return CompositeDecoder.DECODE_DONE
        return elementIndex++
    }

    override fun beginStructure(descriptor: SerialDescriptor): CompositeDecoder =
        ListDecoder(list, descriptor.elementsCount)

    override fun decodeSequentially(): Boolean = true

    override fun decodeCollectionSize(descriptor: SerialDescriptor): Int =
        decodeInt().also { elementsCount = it }
-->

　デコーダの実装では、[Decoder.decodeNotNullMark]をオーバーライドしています。

```kotlin 
    override fun decodeNotNullMark(): Boolean = decodeString() != "NULL"
```

<!--- INCLUDE
}

fun <T> decodeFromList(list: List<Any>, deserializer: DeserializationStrategy<T>): T {
    val decoder = ListDecoder(ArrayDeque(list))
    return decoder.decodeSerializableValue(deserializer)
}

inline fun <reified T> decodeFromList(list: List<Any>): T = decodeFromList(list, serializer())
-->

　NULLではない値とNULL値の両方でNULL可能なプロパティをテストしてみましょう。

```kotlin     
@Serializable
data class Project(val name: String, val owner: User?, val votes: Int?)

@Serializable
data class User(val name: String)

fun main() {
    val data = Project("kotlinx.serialization",  User("kotlin") , null)
    val list = encodeToList(data)
    println(list)
    val obj = decodeFromList<Project>(list)
    println(obj)
}

```

> 完全なコードは[こちら](https://github.com/Kotlin/kotlinx.serialization/blob/master/guide/example/example-formats-13.kt)から取得できます。

　出力では、NOT-NULLマークとNULLマークがどのように使われているかがわかります。

```text
[kotlinx.serialization, !!, kotlin, NULL]
Project(name=kotlinx.serialization, owner=User(name=kotlin), votes=null)
```                      

<!--- TEST -->

### 効率的なバイナリ形式

　これで、効率的なバイナリ形式の例の準備ができました。[java.io.DataOutput]の実装にデータを書き込もうとしています。エンコーダ内の10個の[プリミティブ](https://qiita.com/guijiu/items/036e9169cb6c6a73214f#%E3%83%97%E3%83%AA%E3%83%9F%E3%83%86%E3%82%A3%E3%83%96)のそれぞれに対して、`encodeValue`の代わりに個々の`encodeXxx`関数をオーバーライドしなければなりません。


<!--- INCLUDE 
import kotlinx.serialization.*
import kotlinx.serialization.Serializable
import kotlinx.serialization.descriptors.*
import kotlinx.serialization.encoding.*
import kotlinx.serialization.modules.*
import java.io.*
-->

```kotlin            
class DataOutputEncoder(val output: DataOutput) : AbstractEncoder() {
    override val serializersModule: SerializersModule = EmptySerializersModule
    override fun encodeBoolean(value: Boolean) = output.writeByte(if (value) 1 else 0)
    override fun encodeByte(value: Byte) = output.writeByte(value.toInt())
    override fun encodeShort(value: Short) = output.writeShort(value.toInt())
    override fun encodeInt(value: Int) = output.writeInt(value)
    override fun encodeLong(value: Long) = output.writeLong(value)
    override fun encodeFloat(value: Float) = output.writeFloat(value)
    override fun encodeDouble(value: Double) = output.writeDouble(value)
    override fun encodeChar(value: Char) = output.writeChar(value.toInt())
    override fun encodeString(value: String) = output.writeUTF(value)
    override fun encodeEnum(enumDescriptor: SerialDescriptor, index: Int) = output.writeInt(index)

    override fun beginCollection(descriptor: SerialDescriptor, collectionSize: Int): CompositeEncoder {
        encodeInt(collectionSize)
        return this
    }

    override fun encodeNull() = encodeBoolean(false)
    override fun encodeNotNullMark() = encodeBoolean(true)
}
```

<!--- INCLUDE

fun <T> encodeTo(output: DataOutput, serializer: SerializationStrategy<T>, value: T) {
    val encoder = DataOutputEncoder(output)
    encoder.encodeSerializableValue(serializer, value)
}

inline fun <reified T> encodeTo(output: DataOutput, value: T) = encodeTo(output, serializer(), value)
-->

　デコーダの実装はエンコーダの実装をミラーしており、すべてのプリミティブな `decodeXxx` 関数をオーバーライドします。

```kotlin 
class DataInputDecoder(val input: DataInput, var elementsCount: Int = 0) : AbstractDecoder() {
    private var elementIndex = 0
    override val serializersModule: SerializersModule = EmptySerializersModule
    override fun decodeBoolean(): Boolean = input.readByte().toInt() != 0
    override fun decodeByte(): Byte = input.readByte()
    override fun decodeShort(): Short = input.readShort()
    override fun decodeInt(): Int = input.readInt()
    override fun decodeLong(): Long = input.readLong()
    override fun decodeFloat(): Float = input.readFloat()
    override fun decodeDouble(): Double = input.readDouble()
    override fun decodeChar(): Char = input.readChar()
    override fun decodeString(): String = input.readUTF()
    override fun decodeEnum(enumDescriptor: SerialDescriptor): Int = input.readInt()

    override fun decodeElementIndex(descriptor: SerialDescriptor): Int {
        if (elementIndex == elementsCount) return CompositeDecoder.DECODE_DONE
        return elementIndex++
    }

    override fun beginStructure(descriptor: SerialDescriptor): CompositeDecoder =
        DataInputDecoder(input, descriptor.elementsCount)

    override fun decodeSequentially(): Boolean = true

    override fun decodeCollectionSize(descriptor: SerialDescriptor): Int =
        decodeInt().also { elementsCount = it }

    override fun decodeNotNullMark(): Boolean = decodeBoolean()
}
```

<!--- INCLUDE

fun <T> decodeFrom(input: DataInput, deserializer: DeserializationStrategy<T>): T {
    val decoder = DataInputDecoder(input)
    return decoder.decodeSerializableValue(deserializer)
}

inline fun <reified T> decodeFrom(input: DataInput): T = decodeFrom(input, serializer())

fun ByteArray.toAsciiHexString() = joinToString("") {
    if (it in 32..127) it.toChar().toString() else
        "{${it.toUByte().toString(16).padStart(2, '0').toUpperCase()}}"
}
-->

　これで任意のデータをシリアライズしたり デシリアライズしたりできるようになりました。例えば、[CBOR (experimental)](#cbor実験)や[ProtoBuf (experimental)](#プロトバッファ-実験)で使われていたクラスと同じです。  


```kotlin    
@Serializable
data class Project(val name: String, val language: String)

fun main() {
    val data = Project("kotlinx.serialization", "Kotlin")
    val output = ByteArrayOutputStream()
    encodeTo(DataOutputStream(output), data)
    val bytes = output.toByteArray()
    println(bytes.toAsciiHexString())
    val input = ByteArrayInputStream(bytes)
    val obj = decodeFrom<Project>(DataInputStream(input))
    println(obj)
}
```

> 完全なコードは[こちら](https://github.com/Kotlin/kotlinx.serialization/blob/master/guide/example/example-formats-14.kt)から取得できます。

　ご覧のように、結果は、シリアル化されているデータのみを含む密なバイナリ形式になります。任意のドメイン固有のコンパクトエンコーディングのために簡単に微調整することができます。

```text
{00}{15}kotlinx.serialization{00}{06}Kotlin
Project(name=kotlinx.serialization, language=Kotlin)
```              

<!--- TEST -->                                                         

### フォーマット固有の型

　フォーマットの実装では、Kotlin Serialization のプリミティブ型のリストにないデータ型や、対応する `encodeXxx`/`decodeXxx` 関数を持たないデータ型に対して特別なサポートを提供することがあります。"エンコーダでは、[`encodeSerializableValue(serializer, value)`][Encoder.encodeSerializableValue]関数をオーバーライドすることで実現します。

　DataOutput`フォーマットの例では、[DataOutput][java.io.DataOutput]にはこの目的のための特別なメソッドがあるので、バイトの配列をシリアライズするための特別な効率的なデータパスを提供したいと思うかもしれません。


　型の検出はシリアライズされる `value` の型を確認するのではなく、`serializer` を見ることで行われるので、ビルドインの [KSerializer] インスタンスから `ByteArray` 型を取得する。


> これは重要な違いです。このようにして、私たちのフォーマットの実装は [カスタムシリアライザ](https://qiita.com/guijiu/items/be7fa14c49cb7e3006dd#%E3%82%AB%E3%82%B9%E3%82%BF%E3%83%A0%E3%82%B7%E3%83%AA%E3%82%A2%E3%83%A9%E3%82%A4%E3%82%B6) を適切にサポートしています。



<!--- INCLUDE 
import kotlinx.serialization.*
import kotlinx.serialization.Serializable
import kotlinx.serialization.descriptors.*
import kotlinx.serialization.modules.*
import kotlinx.serialization.encoding.*
import java.io.*
-->

```kotlin         
private val byteArraySerializer = serializer<ByteArray>()
```   

> 特にバイト配列については、組み込みの [ByteArraySerializer][kotlinx.serialization.builtins.ByteArraySerializer()] 関数を使用することもできました。


　[効率的なバイナリフォーマット](#効率的なバイナリ形式)の実装である[Encoder]に対応するコードを追加する。エンコーディングをより効率的にするために、`ByteArray` の実装に `encodeCompactSize` 関数を追加する。 

<!--- INCLUDE
class DataOutputEncoder(val output: DataOutput) : AbstractEncoder() {
    override val serializersModule: SerializersModule = EmptySerializersModule
    override fun encodeBoolean(value: Boolean) = output.writeByte(if (value) 1 else 0)
    override fun encodeByte(value: Byte) = output.writeByte(value.toInt())
    override fun encodeShort(value: Short) = output.writeShort(value.toInt())
    override fun encodeInt(value: Int) = output.writeInt(value)
    override fun encodeLong(value: Long) = output.writeLong(value)
    override fun encodeFloat(value: Float) = output.writeFloat(value)
    override fun encodeDouble(value: Double) = output.writeDouble(value)
    override fun encodeChar(value: Char) = output.writeChar(value.toInt())
    override fun encodeString(value: String) = output.writeUTF(value)
    override fun encodeEnum(enumDescriptor: SerialDescriptor, index: Int) = output.writeInt(index)

    override fun beginCollection(descriptor: SerialDescriptor, collectionSize: Int): CompositeEncoder {
        encodeInt(collectionSize)
        return this
    }

    override fun encodeNull() = encodeBoolean(false)
    override fun encodeNotNullMark() = encodeBoolean(true)
-->

```kotlin
    override fun <T> encodeSerializableValue(serializer: SerializationStrategy<T>, value: T) {
        if (serializer === byteArraySerializer)
            encodeByteArray(value as ByteArray)
        else
            super.encodeSerializableValue(serializer, value)
    }

    private fun encodeByteArray(bytes: ByteArray) {
        encodeCompactSize(bytes.size)
        output.write(bytes)
    }
    
    private fun encodeCompactSize(value: Int) {
        if (value < 0xff) {
            output.writeByte(value)
        } else {
            output.writeByte(0xff)
            output.writeInt(value)
        }
    }            
```

<!--- INCLUDE
}

fun <T> encodeTo(output: DataOutput, serializer: SerializationStrategy<T>, value: T) {
    val encoder = DataOutputEncoder(output)
    encoder.encodeSerializableValue(serializer, value)
}

inline fun <reified T> encodeTo(output: DataOutput, value: T) = encodeTo(output, serializer(), value)

class DataInputDecoder(val input: DataInput, var elementsCount: Int = 0) : AbstractDecoder() {
    private var elementIndex = 0
    override val serializersModule: SerializersModule = EmptySerializersModule
    override fun decodeBoolean(): Boolean = input.readByte().toInt() != 0
    override fun decodeByte(): Byte = input.readByte()
    override fun decodeShort(): Short = input.readShort()
    override fun decodeInt(): Int = input.readInt()
    override fun decodeLong(): Long = input.readLong()
    override fun decodeFloat(): Float = input.readFloat()
    override fun decodeDouble(): Double = input.readDouble()
    override fun decodeChar(): Char = input.readChar()
    override fun decodeString(): String = input.readUTF()
    override fun decodeEnum(enumDescriptor: SerialDescriptor): Int = input.readInt()

    override fun decodeElementIndex(descriptor: SerialDescriptor): Int {
        if (elementIndex == elementsCount) return CompositeDecoder.DECODE_DONE
        return elementIndex++
    }

    override fun beginStructure(descriptor: SerialDescriptor): CompositeDecoder =
        DataInputDecoder(input, descriptor.elementsCount)

    override fun decodeSequentially(): Boolean = true

    override fun decodeCollectionSize(descriptor: SerialDescriptor): Int =
        decodeInt().also { elementsCount = it }

    override fun decodeNotNullMark(): Boolean = decodeBoolean()
-->

　[Decoder]の実装にも同様のコードが追加されています。ここでは、[decodeSerializableValue](https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-core/kotlinx-serialization-core/kotlinx.serialization.encoding/-decoder/decode-serializable-value.html)関数をオーバーライドします。

```kotlin 
    @Suppress("UNCHECKED_CAST")
    override fun <T> decodeSerializableValue(deserializer: DeserializationStrategy<T>, previousValue: T?): T =
        if (deserializer === byteArraySerializer)
            decodeByteArray() as T
        else
            super.decodeSerializableValue(deserializer, previousValue)

    private fun decodeByteArray(): ByteArray {
        val bytes = ByteArray(decodeCompactSize())
        input.readFully(bytes)
        return bytes
    }

    private fun decodeCompactSize(): Int {
        val byte = input.readByte().toInt() and 0xff
        if (byte < 0xff) return byte
        return input.readInt()
    }
```

<!--- INCLUDE
}

fun <T> decodeFrom(input: DataInput, deserializer: DeserializationStrategy<T>): T {
    val decoder = DataInputDecoder(input)
    return decoder.decodeSerializableValue(deserializer)
}

inline fun <reified T> decodeFrom(input: DataInput): T = decodeFrom(input, serializer())

fun ByteArray.toAsciiHexString() = joinToString("") {
    if (it in 32..127) it.toChar().toString() else
        "{${it.toUByte().toString(16).padStart(2, '0').toUpperCase()}}"
}
-->

　これで、いくつかのバイト配列のシリアライズを実行する準備が整いました。

```kotlin    
@Serializable
data class Project(val name: String, val attachment: ByteArray)

fun main() {
    val data = Project("kotlinx.serialization", byteArrayOf(0x0A, 0x0B, 0x0C, 0x0D))
    val output = ByteArrayOutputStream()
    encodeTo(DataOutputStream(output), data)
    val bytes = output.toByteArray()
    println(bytes.toAsciiHexString())
    val input = ByteArrayInputStream(bytes)
    val obj = decodeFrom<Project>(DataInputStream(input))
    println(obj)
}
```

> フルコードは[こちら](https://github.com/Kotlin/kotlinx.serialization/blob/master/guide/example/example-formats-15.kt)から取得することができます。

　ご覧のように、カスタムのバイト配列フォーマットが使用されており、そのサイズを1バイトでコンパクトにエンコードしています。

```text
{00}{15}kotlinx.serialization{04}{0A}{0B}{0C}{0D}
Project(name=kotlinx.serialization, attachment=[10, 11, 12, 13])
```                   

<!--- TEST -->              

---

本章では、[Kotlin Serialization ガイド](https://qiita.com/guijiu/items/097e858e4930b6d7e6c0#kotlin-serialization-%E3%82%AC%E3%82%A4%E3%83%89-%E7%BF%BB%E8%A8%B3)を締めくくります。

<!-- references -->
[RFC 7049]: https://tools.ietf.org/html/rfc7049
[IoT]: https://en.wikipedia.org/wiki/Internet_of_things
[RFC 7049 Major Types]: https://tools.ietf.org/html/rfc7049#section-2.1

<!-- Java references -->
[java.io.DataOutput]: https://docs.oracle.com/javase/8/docs/api/java/io/DataOutput.html

<!--- MODULE /kotlinx-serialization-core -->
<!--- INDEX kotlinx-serialization-core/kotlinx.serialization -->
[serializer]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-core/kotlinx-serialization-core/kotlinx.serialization/../../../kotlinx-serialization-core/kotlinx-serialization-core/kotlinx.serialization/index.html#kotlinx.serialization%2F%2Fserializer%2F%23%2FPointingToDeclaration%2F
[KSerializer]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-core/kotlinx-serialization-core/kotlinx.serialization/-k-serializer/index.html
<!--- INDEX kotlinx-serialization-core/kotlinx.serialization.builtins -->
[kotlinx.serialization.builtins.ByteArraySerializer()]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-core/kotlinx-serialization-core/kotlinx.serialization.builtins/-byte-array-serializer.html
<!--- INDEX kotlinx-serialization-core/kotlinx.serialization.encoding -->
[Encoder]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-core/kotlinx-serialization-core/kotlinx.serialization.encoding/-encoder/index.html
[Decoder]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-core/kotlinx-serialization-core/kotlinx.serialization.encoding/-decoder/index.html
[AbstractEncoder]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-core/kotlinx-serialization-core/kotlinx.serialization.encoding/-abstract-encoder/index.html
[AbstractDecoder]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-core/kotlinx-serialization-core/kotlinx.serialization.encoding/-abstract-decoder/index.html
[AbstractEncoder.encodeValue]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-core/kotlinx-serialization-core/kotlinx.serialization.encoding/-abstract-encoder/encode-value.html
[AbstractDecoder.decodeValue]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-core/kotlinx-serialization-core/kotlinx.serialization.encoding/-abstract-decoder/decode-value.html
[CompositeDecoder.decodeElementIndex]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-core/kotlinx-serialization-core/kotlinx.serialization.encoding/-composite-decoder/decode-element-index.html
[Decoder.beginStructure]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-core/kotlinx-serialization-core/kotlinx.serialization.encoding/-decoder/begin-structure.html
[CompositeDecoder.decodeSequentially]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-core/kotlinx-serialization-core/kotlinx.serialization.encoding/-composite-decoder/decode-sequentially.html
[Encoder.beginCollection]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-core/kotlinx-serialization-core/kotlinx.serialization.encoding/-encoder/begin-collection.html
[CompositeDecoder.decodeCollectionSize]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-core/kotlinx-serialization-core/kotlinx.serialization.encoding/-composite-decoder/decode-collection-size.html
[Encoder.encodeNull]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-core/kotlinx-serialization-core/kotlinx.serialization.encoding/-encoder/encode-null.html
[Encoder.encodeNotNullMark]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-core/kotlinx-serialization-core/kotlinx.serialization.encoding/-encoder/encode-not-null-mark.html
[Decoder.decodeNotNullMark]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-core/kotlinx-serialization-core/kotlinx.serialization.encoding/-decoder/decode-not-null-mark.html
[Encoder.encodeSerializableValue]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-core/kotlinx-serialization-core/kotlinx.serialization.encoding/-encoder/encode-serializable-value.html
[Decoder.decodeSerializableValue]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-core/kotlinx-serialization-core/kotlinx.serialization.encoding/-decoder/decode-serializable-value.html
<!--- MODULE /kotlinx-serialization-properties -->
<!--- INDEX kotlinx-serialization-properties/kotlinx.serialization.properties -->
[kotlinx.serialization.properties.Properties]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-properties/kotlinx-serialization-properties/kotlinx.serialization.properties/-properties/index.html
<!--- MODULE /kotlinx-serialization-protobuf -->
<!--- INDEX kotlinx-serialization-protobuf/kotlinx.serialization.protobuf -->
[ProtoBuf]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-protobuf/kotlinx-serialization-protobuf/kotlinx.serialization.protobuf/-proto-buf/index.html
[ProtoBuf.encodeToByteArray]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-protobuf/kotlinx-serialization-protobuf/kotlinx.serialization.protobuf/-proto-buf/encode-to-byte-array.html
[ProtoBuf.decodeFromByteArray]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-protobuf/kotlinx-serialization-protobuf/kotlinx.serialization.protobuf/-proto-buf/decode-from-byte-array.html
[ProtoNumber]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-protobuf/kotlinx-serialization-protobuf/kotlinx.serialization.protobuf/-proto-number/index.html
[ProtoType]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-protobuf/kotlinx-serialization-protobuf/kotlinx.serialization.protobuf/-proto-type/index.html
[ProtoIntegerType]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-protobuf/kotlinx-serialization-protobuf/kotlinx.serialization.protobuf/-proto-integer-type/index.html
[ProtoIntegerType.DEFAULT]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-protobuf/kotlinx-serialization-protobuf/kotlinx.serialization.protobuf/-proto-integer-type/-d-e-f-a-u-l-t/index.html
[ProtoIntegerType.SIGNED]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-protobuf/kotlinx-serialization-protobuf/kotlinx.serialization.protobuf/-proto-integer-type/-s-i-g-n-e-d/index.html
[ProtoIntegerType.FIXED]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-protobuf/kotlinx-serialization-protobuf/kotlinx.serialization.protobuf/-proto-integer-type/-f-i-x-e-d/index.html
<!--- MODULE /kotlinx-serialization-cbor -->
<!--- INDEX kotlinx-serialization-cbor/kotlinx.serialization.cbor -->
[Cbor]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-cbor/kotlinx-serialization-cbor/kotlinx.serialization.cbor/-cbor/index.html
[Cbor.encodeToByteArray]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-cbor/kotlinx-serialization-cbor/kotlinx.serialization.cbor/-cbor/encode-to-byte-array.html
[Cbor.decodeFromByteArray]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-cbor/kotlinx-serialization-cbor/kotlinx.serialization.cbor/-cbor/decode-from-byte-array.html
[CborBuilder.ignoreUnknownKeys]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-cbor/kotlinx-serialization-cbor/kotlinx.serialization.cbor/-cbor-builder/index.html#kotlinx.serialization.cbor%2FCborBuilder%2FignoreUnknownKeys%2F%23%2FPointingToDeclaration%2F
[ByteString]: https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-cbor/kotlinx-serialization-cbor/kotlinx.serialization.cbor/-byte-string/index.html
<!--- END -->