8
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Kotlin Serialization ガイド 第3章 シリアライザ

Last updated at Posted at 2021-02-04

#はじめに
 Kotlin Serialization ガイドの目次に基づいて「Kotlin Serialization guide」を訳しています。
 なお、翻訳にはDeepLの力を99%借りています。
 もし、一緒に「Kotlin Serialization ガイド」の翻訳をして下さる方がいらっしゃいましたら、コメント欄などから連絡をください。

##第3章 シリアライザ <原文
Latest commit 728e220 on 24 Nov 2020版

Kotlin Serialization Guideの第3章です。この章では、シリアライザについて詳しく見ていき、カスタムシリアライザの書き方を見ていきます。

目次

##シリアライザの紹介

 JSON のようなフォーマットでは、オブジェクトの特定の出力バイトへの encoding を制御しますが、オブジェクトがどのように構成されたプロパティに分解されるかは serializer によって制御されます。これまでは、シリアライゼーション クラスで説明したように、@Serializableアノテーションを使用して自動生成されたシリアライザを使用したり、組み込みクラスで示した組み込みのシリアライザを使用したりしてきました。

 興味を与える例として、rgb バイトを格納した整数値を持つ以下の Color クラスを見てみよう。

@Serializable
class Color(val rgb: Int)

fun main() {
    val green = Color(0x00ff00)
    println(Json.encodeToString(green))
}  

完全なコードはこちらから取得できます。

 デフォルトでは、このクラスは rgb プロパティを JSON にシリアライズします。

{"rgb":65280}

###プラグイン生成シリアライザ

 前の例の Color クラスのように @Serializable アノテーションを付けたクラスはすべて、Kotlin Serialization コンパイラプラグインによって自動的に生成される KSerializer インターフェイスのインスタンスを取得します。

 シリアライズされたクラスの構造を記述した descriptor プロパティを調べることができます。これについての詳細は次のセクションで説明します。

fun main() {
    val colorSerializer: KSerializer<Color> = Color.serializer()
    println(colorSerializer.descriptor)
} 

完全なコードはこちらから取得できます。

Color(rgb: kotlin.Int)

 このシリアライザは、Color クラス自体がシリアライズされたとき、または他のクラスのプロパティとして使用されたときに、Kotlin Serialization フレームワークによって自動的に取得され、使用されます。

シリアライズ可能なクラスのコンパニオンオブジェクトに対して独自の関数 serializer() を定義することはできません。

###プラグイン生成ジェネリックシリアライザ

 ジェネリッククラス で示した Box クラスのような汎用クラスの場合、自動的に生成される .serializer() 関数は対応するクラスの型パラメータの数だけパラメータを受け付ける。これらのパラメータはKSerializer型であるため、ジェネリッククラスのシリアライザのインスタンスを作成する際には、実際の型引数のシリアライザを指定しなければならない。

@Serializable           
@SerialName("Box")
class Box<T>(val contents: T)    

fun main() {
    val boxedColorSerializer = Box.serializer(Color.serializer())
    println(boxedColorSerializer.descriptor)
} 

完全なコードはこちらから取得できます。

 ご覧の通り、具体的な Box<Color> をシリアライズするためにシリアライザがインスタンス化されています。

Box(contents: Color)

###組み込みプリミティブシリアライザ

 プリミティブ組み込みクラスのシリアライザは、.serializer()拡張子を使って取得することができます。

fun main() {
    val intSerializer: KSerializer<Int> = Int.serializer()
    println(intSerializer.descriptor)
}

フルコードはこちらから取得することができます。

###コレクションシリアライザを構築

 ビルトインコレクションシリアライザは、必要に応じて、対応する関数ListSerializer()SetSerializer()MapSerializer()などを使って明示的に構築しなければなりません。これらのクラスは汎用的なので、シリアライザをインスタンス化するためには、対応する数の型パラメータに対応するシリアライザを提供しなければなりません。例えば、以下のように List<String> のシリアライザを生成することができます。

fun main() {   
    val stringListSerializer: KSerializer<List<String>> = ListSerializer(String.serializer()) 
    println(stringListSerializer.descriptor)
}

フルコードはこちらから取得することができます。

###トップレベルのシリアライザ機能を使用

 疑わしい場合は、トップレベルの汎用 serializer<T>()関数を使って、ソースコード中の任意のKotlin型のシリアライザを取得することができます。

@Serializable            
@SerialName("Color")
class Color(val rgb: Int)

fun main() {        
    val stringToColorMapSerializer: KSerializer<Map<String, Color>> = serializer()
    println(stringToColorMapSerializer.descriptor)
}

フルコードはこちらから取得することができます。

##カスタムシリアライザ

 プラグイン生成のシリアライザは便利だが、Color のようなクラスのために必要なJSONを生成できないかもしれない。代替案を検討してみましょう。

###プリミティブシリアライザ

 緑の色を "00ff00" で表現した16進文字列として Color クラスをシリアライズしたい。これを実現するために、Color クラスのための KSerializer インターフェースを実装したオブジェクトを書く。

object ColorAsStringSerializer : KSerializer<Color> {
    override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("Color", PrimitiveKind.STRING)

    override fun serialize(encoder: Encoder, value: Color) {
        val string = value.rgb.toString(16).padStart(6, '0')
        encoder.encodeString(string)
    }

    override fun deserialize(decoder: Decoder): Color {
        val string = decoder.decodeString()
        return Color(string.toInt(16))
    }
}

 シリアライザには3つの必要なピースがあります。

  • serialize 関数は、SerializationStrategy を実装しています。 Encoder のインスタンスとシリアライズする値を受け取ります。 この関数は EncoderencodeXxx 関数を使用して値をプリミティブのシーケンスとして表現します。シリアライズでサポートされるプリミティブの型ごとに encodeXxx がある。 この例では、encodeStringを使用しています。

  • deserialize 関数は、DeserializationStrategy を実装しています。 これは Decoder のインスタンスを受け取り、デシリアライズされた値を返します。これは DecoderdecodeXxx 関数を利用しますが、これは Encoder の対応する関数をミラーリングしたものです。 この例では、decodeStringが使われています。

  • descriptorプロパティは、encodeXxxdecodeXxx 関数がどのようなエンコーディング/デコーディングメソッドを呼び出すかをフォーマットの実装が事前に知っているように、正確に何をするのかを忠実に説明しなければなりません。いくつかのフォーマットでは、シリアライズされたデータのスキーマを生成するためにこれを使うこともあります。プリミティブシリアライズの場合、PrimitiveSerialDescriptor関数はシリアライズされる型のユニークな名前を指定して使用しなければならない。PrimitiveKind は、実装で使用される特定の encodeXxx/decodeXxx メソッドを記述する。

このように、descriptorがエンコード/デコード方法に対応していない場合、結果として得られるコードの振る舞いは不特定であり、将来の更新で任意に変更される可能性があります。

 次のステップはシリアライザをクラスにバインドすることです。これは、withプロパティ値を追加することで、@Serializableアノテーションで行います。

@Serializable(with = ColorAsStringSerializer::class)
class Color(val rgb: Int)

 これで、先ほどと同じように Color クラスをシリアライズすることができるようになりました。

fun main() {
    val green = Color(0x00ff00)
    println(Json.encodeToString(green))
}  

完全なコードはこちらから取得できます。

 シリアル表現を16進数の文字列として取得します。

"00ff00"

 デシリアライズも deserialize メソッドを実装しているので簡単である。

@Serializable(with = ColorAsStringSerializer::class)
class Color(val rgb: Int)

fun main() {
    val color = Json.decodeFromString<Color>("\"00ff00\"")
    println(color.rgb) // prints 65280 
}  

フルコードはこちらから取得することができます。

 また、Color プロパティを持つ別のクラスをシリアライズまたはデシリアライズしても動作します。

@Serializable(with = ColorAsStringSerializer::class)
data class Color(val rgb: Int)

@Serializable 
data class Settings(val background: Color, val foreground: Color)

fun main() {
    val data = Settings(Color(0xffffff), Color(0))
    val string = Json.encodeToString(data)
    println(string)
    require(Json.decodeFromString<Settings>(string) == data)
}  

フルコードはこちらから取得することができます。

 色`プロパティはいずれも文字列としてシリアライズされる。

{"background":"ffffff","foreground":"000000"}

###サロゲート経由のコンポジットシリアライザ

 さて、私たちの課題は Color をシリアライズして、JSON で r, g, b の 3 つのプロパティを持つクラスであるかのように JSON で表現し、JSON がオブジェクトとしてエンコードするようにすることです。これを実現する最も簡単な方法は、シリアル化に使用する Color を模倣した surrogate クラスを定義することです。また、このサロゲートクラスの SerialNameColor を設定します。これで、この名前を使うフォーマットがあれば、サロゲートは Color クラスであるかのように見える。サロゲートクラスは private にすることができ、その init ブロックでクラスのシリアル表現に対するすべての制約を強制することができる。

@Serializable
@SerialName("Color")
private class ColorSurrogate(val r: Int, val g: Int, val b: Int) {
    init {     
        require(r in 0..255 && g in 0..255 && b in 0..255)
    }
}

クラス名を使用する場所の例は、ポリモーフィズムの章のカスタムサブクラスシリアル名の項に記載されています。

 関数 ColorSurrogate.serializer() を使って、プラグインが生成したシリアライザを取得することができます。

 オリジナルの Color クラスのための KSerializer の実装は、ColorColorSurrogate の変換を行う。しかし、実際のシリアライズロジックは ColorSurrogate.serializer() に委譲して encodeSerializableValuedecodeSerializableValue を使用し、サロゲート用に自動生成された SerialDescriptor を完全に再利用します。

object ColorSerializer : KSerializer<Color> {
    override val descriptor: SerialDescriptor = ColorSurrogate.serializer().descriptor

    override fun serialize(encoder: Encoder, value: Color) {
        val surrogate = ColorSurrogate((value.rgb shr 16) and 0xff, (value.rgb shr 8) and 0xff, value.rgb and 0xff)
        encoder.encodeSerializableValue(ColorSurrogate.serializer(), surrogate)
    }

    override fun deserialize(decoder: Decoder): Color {
        val surrogate = decoder.decodeSerializableValue(ColorSurrogate.serializer())
        return Color((surrogate.r shl 16) or (surrogate.g shl 8) or surrogate.b)
    }
}

ColorSerializer シリアライザを Color クラスにバインドする。

@Serializable(with = ColorSerializer::class)
class Color(val rgb: Int)

 これで、Color クラスのシリアライズの結果を楽しむことができる。

フルコードはこちらから取得することができます。

{"r":0,"g":255,"b":0}

###手書き複合シリアライザ

 サロゲートソリューションが合わない場合もあります。おそらく、追加の割り当てによるパフォーマンスへの影響を避けたい場合や、結果として得られるシリアル表現のために、設定可能で動的なプロパティのセットが欲しい場合があるでしょう。このような場合は、生成されたシリアライザの動作を模倣するクラスシリアライザを手動で記述する必要があります。

object ColorAsObjectSerializer : KSerializer<Color> {

 少しずつ紹介していきましょう。まず、buildClassSerialDescriptorビルダーを使って記述子を定義します。ビルダーDSLのelement関数は、対応するフィールドのシリアライザをタイプ別に自動的に取得します。要素の順序は重要です。これらの要素はゼロから始まるインデックスが付けられます。

    override val descriptor: SerialDescriptor =
        buildClassSerialDescriptor("Color") {
            element<Int>("r")
            element<Int>("g")
            element<Int>("b")
        }

ここでは「要素」は一般的な用語です。記述子の要素が何であるかは,そのSerialKindに依存します。クラス記述子の要素はそのプロパティ、enum記述子の要素はそのケースなどです。

 次に、ブロック内の CompositeEncoder へのアクセスを提供する encodeStructure DSLを用いて serialize 関数を書く。EncoderCompositeEncoder の違いは、後者には前者の encodeXxx 関数に対応する encodeXxxElement 関数があることである。これらの関数は記述子と同じ順番で呼ばれなければなりません。

    override fun serialize(encoder: Encoder, value: Color) =
        encoder.encodeStructure(descriptor) {
            encodeIntElement(descriptor, 0, (value.rgb shr 16) and 0xff)
            encodeIntElement(descriptor, 1, (value.rgb shr 8) and 0xff)
            encodeIntElement(descriptor, 2, value.rgb and 0xff)
        }

 最も複雑なコードは deserialize 関数である。これはJSONのように、任意の順序でプロパティをデコードできるフォーマットをサポートしなければなりません。これは、CompositeDecoder へのアクセスを得るための decodeStructure の呼び出しから始まる。その中で、次の要素のインデックスをデコードするために decodeElementIndex を繰り返し呼び出すループを書き、次に、この例では decodeIntElement を使って対応する要素をデコードし、最後にCompositeDecoder.DECODE_DONE が発生した。その中で、decodeElementIndexを繰り返し呼び出して次の要素のインデックスをデコードし、対応する要素をdecodeIntElementを使ってデコードし、最後に CompositeDecoder.DECODE_DONE に遭遇したときにループを終了させるループを書いています。

    override fun deserialize(decoder: Decoder): Color =
        decoder.decodeStructure(descriptor) {
            var r = -1
            var g = -1
            var b = -1
            while (true) {
                when (val index = decodeElementIndex(descriptor)) {
                    0 -> r = decodeIntElement(descriptor, 0)
                    1 -> g = decodeIntElement(descriptor, 1)
                    2 -> b = decodeIntElement(descriptor, 2)
                    CompositeDecoder.DECODE_DONE -> break
                    else -> error("Unexpected index: $index")
                }
            }
            require(r in 0..255 && g in 0..255 && b in 0..255)
            Color((r shl 16) or (g shl 8) or b)
        }

 ここで、できあがったシリアライザを Color クラスにバインドし、そのシリアライゼーション/デシリアライゼーションをテストする。

@Serializable(with = ColorAsObjectSerializer::class)
data class Color(val rgb: Int)

fun main() {
    val color = Color(0x00ff00)
    val string = Json.encodeToString(color) 
    println(string)
    require(Json.decodeFromString<Color>(string) == color)
}  

フルコードはこちらから取得することができます。

 以前と同様に、Color クラスは 3 つのキーを持つ JSON オブジェクトとして表現されます。

{"r":0,"g":255,"b":0}

###逐次復号化プロトコル(実験)

 前節の deserialize 関数の実装は、どのような形式でも動作する。しかし、いくつかのフォーマットでは、すべての複雑なデータを常に順番に保存しているものと、時々しか保存していないものがあります(JSONは常にコレクションを順番に保存しています)。これらのフォーマットでは、ループ内で decodeElementIndex を呼び出す複雑なプロトコルは不要で、CompositeDecoder.decodeSequentially 関数が true を返す場合は、より高速な実装を使うことができます。 プラグインが生成するシリアライザは、実際には以下のコードと概念的に似ています。

    override fun deserialize(decoder: Decoder): Color =
        decoder.decodeStructure(descriptor) {
            var r = -1
            var g = -1
            var b = -1     
            if (decodeSequentially()) { // sequential decoding protocol
                r = decodeIntElement(descriptor, 0)           
                g = decodeIntElement(descriptor, 1)  
                b = decodeIntElement(descriptor, 2)
            } else while (true) {
                when (val index = decodeElementIndex(descriptor)) {
                    0 -> r = decodeIntElement(descriptor, 0)
                    1 -> g = decodeIntElement(descriptor, 1)
                    2 -> b = decodeIntElement(descriptor, 2)
                    CompositeDecoder.DECODE_DONE -> break
                    else -> error("Unexpected index: $index")
                }
            }
            require(r in 0..255 && g in 0..255 && b in 0..255)
            Color((r shl 16) or (g shl 8) or b)
        }

フルコードはこちらから取得することができます。

###サードパーティクラスのシリアライズ

 時々、アプリケーションはシリアル化できない外部の型を使用しなければならないことがあります。例として、java.util.Dateを使ってみましょう。先ほどと同様に、このクラスのためにKSerializerの実装を書くことから始めます。私たちの目標は、プリミティブシリアライザのセクションのアプローチに従って、DateをLongのミリ秒数でシリアライズすることです。

以下の節では、どのような種類の Date シリアライザでも動作します。例えば、Date をオブジェクトとしてシリアライズしたい場合は、サロゲートを使った複合シリアライザのセクションのアプローチを使います。サードパーティの Kotlin クラスをシリアライズする必要がある場合は、他のkotlinクラスのための外部シリアライザの導出(実験)も参照してください。

object DateAsLongSerializer : KSerializer<Date> {
    override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("Date", PrimitiveKind.LONG)
    override fun serialize(encoder: Encoder, value: Date) = encoder.encodeLong(value.time)
    override fun deserialize(decoder: Decoder): Date = Date(decoder.decodeLong())
}

 Date のソースコードを制御できないため、DateAsLongSerializerシリアライザをDate` クラスに @Serializable アノテーションでバインドすることはできません。これを回避する方法はいくつかあります。

###シリアライザを手動で渡す

 すべての encodeToXxx および decodeFromXxx 関数は、最初のシリアライザパラメータを持つオーバーロードを持っています。シリアライズ可能ではないクラス、例えば Date のような最上位クラスがシリアライズされる場合は、これらを使うことができます。

fun main() {                                              
    val kotlin10ReleaseDate = SimpleDateFormat("yyyy-MM-ddX").parse("2016-02-15+00") 
    println(Json.encodeToString(DateAsLongSerializer, kotlin10ReleaseDate))    
}

フルコードはこちらから取得することができます。

1.45549E+12

###プロパティにシリアライザを指定

 Date`のようなシリアライズ可能ではないクラスのプロパティがシリアライズ可能なクラスの一部としてシリアライズされている場合、そのシリアライザを提供しなければなりません。これは、プロパティの @Serializable アノテーションを使用して行います。

@Serializable          
class ProgrammingLanguage(
    val name: String,
    @Serializable(with = DateAsLongSerializer::class)
    val stableReleaseDate: Date
)

fun main() {
    val data = ProgrammingLanguage("Kotlin", SimpleDateFormat("yyyy-MM-ddX").parse("2016-02-15+00"))
    println(Json.encodeToString(data))
}

フルコードはこちらから取得することができます。

 stableReleaseDateプロパティは、指定したシリアライゼーションの方針(the serialization strategy)でシリアライズされます。

{"name":"Kotlin","stableReleaseDate":1455494400000}

###ファイルにシリアライザを指定

 Date` のような特定の型のシリアライザは、ファイルの先頭にあるファイルレベルのUseSerializersアノテーションでソースコードファイル全体に対して指定することができます。

@file:UseSerializers(DateAsLongSerializer::class)

 これで、追加のアノテーションなしに Date プロパティをシリアライズ可能なクラスで使用できるようになりました。

@Serializable          
class ProgrammingLanguage(val name: String, val stableReleaseDate: Date)

fun main() {
    val data = ProgrammingLanguage("Kotlin", SimpleDateFormat("yyyy-MM-ddX").parse("2016-02-15+00"))
    println(Json.encodeToString(data))
}

フルコードはこちらから取得することができま。

{"name":"Kotlin","stableReleaseDate":1455494400000}

###ジェネリックタイプ用のカスタムシリアライザ

 以下に汎用の Box<T> クラスの例を見てみよう。これは、カスタムのシリアライズ戦略を持つ予定なので、@Serializable(with = BoxSerializer::class)とマークされている。

@Serializable(with = BoxSerializer::class)
data class Box<T>(val contents: T) 

 この章の Color 型の例で見たように、通常の型に対する KSerializer の実装は object として書かれている。一般的なクラスのシリアライザは、その一般的なパラメータのためのシリアライザを使ってインスタンス化される。これについてはプラグインで生成された汎用シリアライザのセクションで見た。ジェネリッククラス用のカスタムシリアライザは、その型が持つジェネリックパラメータの数だけKSerializerパラメータを受け付けるコンストラクタを持つ class でなければならない。ここでは、Box<T> のシリアライザを書いてみましょう。

class BoxSerializer<T>(private val dataSerializer: KSerializer<T>) : KSerializer<Box<T>> {
    override val descriptor: SerialDescriptor = dataSerializer.descriptor
    override fun serialize(encoder: Encoder, value: Box<T>) = dataSerializer.serialize(encoder, value.contents)
    override fun deserialize(decoder: Decoder) = Box(dataSerializer.deserialize(decoder))
}

 これで、Box<Project>をシリアライズ、デシリアライズできるようになりました。

@Serializable
data class Project(val name: String)

fun main() {
    val box = Box(Project("kotlinx.serialization"))
    val string = Json.encodeToString(box)
    println(string)
    println(Json.decodeFromString<Box<Project>>(string))
}

フルコードはこちらから取得することができます。

 結果のJSONは Project クラスが直接シリアライズされたように見えます。

{"name":"kotlinx.serialization"}
Box(contents=Project(name=kotlinx.serialization))

###フォーマット固有のシリアライザ

 上記のカスタムシリアライザは、どのフォーマットでも同じように動作しました。しかし、シリアライザの実装で利用したいフォーマット固有の機能があるかもしれません。

 この章では、コンテキストに基づいてシリアライズ戦略を微調整するための一般的なアプローチを進めます。

##コンテクスチュアルシリアライズ

 カスタムシリアライズ戦略を指定するこれまでのアプローチは、コンパイル時に完全に定義される static でした。例外はシリアライザを手動で渡すというアプローチでしたが、トップレベルのオブジェクトに対してしか動作しませんでした。シリアライズされたオブジェクトツリーの奥にあるオブジェクトのシリアライズ戦略を実行時に変更する必要があるかもしれません。例えば、java.util.DateをJSON形式でISO 8601の文字列で表現したい場合と、データをシリアライズするプロトコルのバージョンに応じて長い整数で表現したい場合があります。これは_contextual_シリアライズと呼ばれ、組み込みのContextualSerializerクラスでサポートされています。通常はこのシリアライザクラスを明示的に使う必要はありません。Contextualアノテーションは @Serializable(with = ContextualSerializer::class) アノテーションへのショートカットを提供し、UseContextualSerializationアノテーションはUseSerializersアノテーションと同様にファイルレベルで使用できます。前者を活用した例を見てみましょう。

@Serializable          
class ProgrammingLanguage(
    val name: String,
    @Contextual 
    val stableReleaseDate: Date
)

 このクラスを実際にシリアライズするためには、encodeToXxx/decodeFromXxx` 関数を呼び出す際に対応するコンテキストを指定しなければなりません。これがないと、"Serializer for class 'Date' is not found" という例外が発生します。

その例外を発生させる例については、こちらを参照してください。

###シリアライザモジュール

 コンテキストを提供するために、実行時にどのシリアライザを使用してどのコンテキストでシリアライズ可能なクラスをシリアライズするかを記述するSerializersModuleインスタンスを定義します。これは、シリアライザを登録するためのSerializersModuleBuilder DSLを提供するSerializersModule {}ビルダー関数を使用して行います。以下の例では、シリアライザでcontextual関数を使用しています。このシリアライザが定義されている対応するクラスは、reified type パラメータを介して自動的に取得されます。

private val module = SerializersModule { 
    contextual(DateAsLongSerializer)
}

 次に、Json {}ビルダー関数とserializersModuleプロパティを使用して、このモジュールでJsonフォーマットのインスタンスを作成します。

カスタムJSON設定の詳細は、JSON設定のセクションに記載されています。

val format = Json { serializersModule = module }

 これで、この format でデータをシリアライズすることができるようになりました。

fun main() {
    val data = ProgrammingLanguage("Kotlin", SimpleDateFormat("yyyy-MM-ddX").parse("2016-02-15+00"))
    println(format.encodeToString(data))
}

フルコードはこちらから取得することができます。

{"name":"Kotlin","stableReleaseDate":1455494400000}

シリアライズモジュールの詳細は、ポリモフィズムの章のライブラリのシリアライザモジュールのマージの項に記載されています。

##他のKotlinクラスのための外部シリアライザの導出(実験)

 シリアライズするサードパーティのクラスがプロパティのみのプライマリコンストラクタを持つKotlinクラスで、@Serializableのようなクラスであれば、forClass`プロパティを持つオブジェクトのSerializerアノテーションを使って、_external_シリアライザを生成することができます。

// NOT @Serializable
class Project(val name: String, val language: String)
                           
@Serializer(forClass = Project::class)
object ProjectSerializer

 この章で説明したアプローチのいずれかを使用して、このシリアライザをクラスにバインドする必要があります。この例では 「シリアライザを手動で渡す」のアプローチに従います。

fun main() {
    val data = Project("kotlinx.serialization", "Kotlin")
    println(Json.encodeToString(ProjectSerializer, data))    
}

フルコードはこちらから取得することができます。

 これはすべての Project プロパティをシリアライズして取得する。

{"name":"kotlinx.serialization","language":"Kotlin"}

###外部シリアライズはプロパティを使用

 "先ほど見たように、通常の @Serializer アノテーションはシリアライザを作成し、「バッキングフィールドをシリアライズする」のようにしています。Serializer(forClass = ....)` を使った _External_シリアライズはバッキングフィールドにアクセスできず、動作が異なります。これは、セッターを持つ、あるいはプライマリコンストラクタの一部である accessible プロパティのみをシリアライズします。 次の例はこれを示しています。"

// NOT @Serializable, will use external serializer
class Project(
    // val in a primary constructor -- serialized
    val name: String
) {
    var stars: Int = 0 // property with getter & setter -- serialized
 
    val path: String // getter only -- not serialized
        get() = "kotlin/$name"                                         

    private var locked: Boolean = false // private, not accessible -- not serialized 
}              

@Serializer(forClass = Project::class)
object ProjectSerializer

fun main() {
    val data = Project("kotlinx.serialization").apply { stars = 9000 }
    println(Json.encodeToString(ProjectSerializer, data))
}

フルコードはこちらから取得することができます

 出力は以下のようになります。

{"name":"kotlinx.serialization","stars":9000}

次の章では、ポリモフィズムを取り上げます。

8
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?