ユーザ定義クラスのカスタムSerializer(Primitive版)

投稿日:  更新日:

Kotlin serializationでJSONをパース(JSONの記述⇔クラスのオブジェクト)する場合に、一般クラスは未対応です。

例えば、データクラス(コンストラクタの引数でプロパティを指定するクラス)以外の、ユーザ定義のクラスはパース出来ません。また、ライブラリ提供のクラスもパース出来ません。

対応させるためには、そのクラスのカスタムSerializerを作成します。

そして、相互変換する方法をプログラマー側で定義します。

カスタムSerializer(ユーザ定義クラス、Primitive版)の作成方法をまとめます。

※環境:Android Studio Meerkat | 2024.3.1
    Kotlin 2.0.0
    Kotlin serialization json 1.7.1

スポンサーリンク

標準で対応する型、しない型

Kotlin serializationでJSONをパース(JSONの記述⇔クラスのオブジェクト)する場合に、標準で対応する型は次の通りです。

  • ・Int,Long,Float,Double,Booleanなど(プリミティブ型)
  • ・String(文字列)※プリミティブ型へ含める場合もある
  • ・Array(配列)
  • ・List,Map,Set(コレクション型)
  • ・Enum(列挙型)
  • ・データクラス

上記以外の一般クラス(ユーザ定義のクラス、ライブラリ提供のクラス)は未対応です。

例えば、次のようなクラスは標準でパース出来ません。

class LogTime(hour: Int, min: Int, sec: Int) {
    val hh: Int = hour
    val mm: Int = min
    val ss: Int = sec

    override fun toString() = "%s:%s:%s".format(hh, mm, ss)
}

一般クラスをパースするには、そのクラスのカスタムSerializerが必要です。

スポンサーリンク

Serializerの種類

Serializerは、表のような種類(書き方)があります。

特徴シリアル化の構成
PrimitivePrimitive型(Stringを含む)限定PrimitiveSerialDescriptor
Delegating定義済みのSerializerへ委任するSerialDescriptor
Surrogate代理クラスを立て(作成し)、
そのクラスのSerializerに代行させる
SerialDescriptor
Handwritten全ての要素のシリアル化を細かく定義buildClassSerialDescriptor

この記事で取り上げるのは、Primitive版です。
※種類は「Custom serializers」に紹介されています。

スポンサーリンク

カスタムSerializerの作成

Serializer(KSerializerインターフェース)は「Kotlin serializerがJSONをパースする際に呼び出される関数」を持ちます。

object XXXSerializer : KSerializer<XXX> {
    override val descriptor: SerialDescriptor
        get() = TODO("識別子を実装")

    override fun serialize(encoder: Encoder, value: XXX) {
        // Json.encodeToString( )で呼び出される
        // Obj->JSONの変換を実装
    }

    override fun deserialize(decoder: Decoder): XXX {
        // Json.decodeFromString( )で呼び出される
        // JSON->Objの変換を実装
        return XXX()
    }
}
 KSerializer#serialize( ) 

「クラスのオブジェクト⇒JSONの記述」で呼び出し

 KSerializer#deserialize( ) 

「JSONの記述⇒クラスのオブジェクト」で呼び出し

ですので、カスタムSerializerは、このKSerializerインターフェースを継承して、相互変換の方法をserialiseとdesirializeへ実装します。

object LogTimeAsStringSerializer : KSerializer<LogTime> {
    override val descriptor: SerialDescriptor =
        PrimitiveSerialDescriptor(
            "com.example.LogTimeAsStringSerializer", // Serializer名(一意に識別)
            PrimitiveKind.STRING                     // シリアル化の構成
        )

    // Obj->JSONの変換
    override fun serialize(encoder: Encoder, value: LogTime) {
        val _timeStr = "%s:%s:%s".format(value.hh, value.mm, value.ss)
        encoder.encodeString(_timeStr)
    }

    // JSON->Objの変換
    override fun deserialize(decoder: Decoder): LogTime {
        val _value = decoder.decodeString()
        val _hhmmss = _value.split(":")
        val _hh = _hhmmss[0].toInt()
        val _mm = _hhmmss[1].toInt()
        val _ss = _hhmmss[2].toInt()
       return LogTime(_hh, _mm, _ss)
    }
}
スポンサーリンク

SerialDescriptor(シリアル化の構成)

SerialDescriptorでシリアル化の構成を定義しています。つまり、「JSONでどのように表現するか!」です。

ここでは、対象クラスをプリミティブ型のString(PrimitiveKind.STRING)で表現します。

定義にPrimitiveSerialDescriptor( )を使います。

    override val descriptor: SerialDescriptor =
        PrimitiveSerialDescriptor(
            "com.example.LogTimeAsStringSerializer", // Serializer名(一意に識別)
            PrimitiveKind.STRING                     // シリアル化の構成
        )

ちなにみ、次にあげるString以外のプリミティブ型が用意されています。

 public sealed class PrimitiveKind : SerialKind() {
    public object BOOLEAN : PrimitiveKind()
    public object BYTE : PrimitiveKind()
    public object CHAR : PrimitiveKind()
    public object SHORT : PrimitiveKind()
    public object INT : PrimitiveKind()
    public object LONG : PrimitiveKind()
    public object FLOAT : PrimitiveKind()
    public object DOUBLE : PrimitiveKind()
    public object STRING : PrimitiveKind()
 }

deserialize(JSON⇒Objの変換方法)

Decoder#decodeString( )により文字列(value)を取り出し、文字列より得られた値(_hh,_mm,_ss)からオブジェクトを作ります。

    override fun deserialize(decoder: Decoder): LogTime {
        val _value = decoder.decodeString()
        val _hhmmss = _value.split(":")
        val _hh = _hhmmss[0].toInt()
        val _mm = _hhmmss[1].toInt()
        val _ss = _hhmmss[2].toInt()
       return LogTime(_hh, _mm, _ss)
    }

serialize(Obj⇒JSONの変換方法)

オブジェクトを文字列に変換し、Encoder#encodeString( )によりJSONへ文字列(value)として書き出します。

    override fun serialize(encoder: Encoder, value: LogTime) {
        val _timeStr = "%s:%s:%s".format(value.hh, value.mm, value.ss)
        encoder.encodeString(_timeStr)
    }
スポンサーリンク

カスタムSerializerの使用

カスタムSerializerはアノテーション@Serializableの引数で登録します。

Android Studioはビルドをする際に、@Serializable付きのクラスを認識して、対象クラスのJavaバイナリへ、引数のカスタムSerializerクラスを埋め込みます。

@Serializable(LogTimeJsonSerializer::class)
class LogTime(hour: Int, min: Int, sec: Int) {
    val hh: Int = hour
    val mm: Int = min
    val ss: Int = sec

    override fun toString() = "%s:%s:%S".format(hh, mm, ss)
}

@Serializable
data class Person(
    val id: Int,
    val name: String,
    @SerialName("position") val post: String,  // JSONのキーに別名を定義
    val times: LogTime
)

JSON⇒Obj変換

Json#decodeFromStringを呼び出します。

内部で対象クラスのdeserialize( )が呼び出されます。

    val _jPerson = """
            {
                "id":1,
                "name":"Android",
                "position":"Manager",
                "times":"8:55:22"
            }
            """.replace("\\s+".toRegex(), "") // 空白の削除
    val _oPerson = Json.decodeFromString<Person>(_jPerson)

    Log.i(TAG, "(Serialization) Person Obj  = ${_oPerson}")
(Serialization) Person Obj  = Person(id=1, name=Android, post=Manager, times=8:55:22)

Obj⇒JSON変換

Json#encodeToStringを呼び出します。

内部で対象クラスのserialize( )が呼び出されます。

    val _oPerson = Person(
        id = 2, name = "Droid", post = "Staff",
        times = LogTime(12, 2, 0)
    )
    val _jPerson = Json.encodeToString(_oPerson)

    Log.i(TAG, "(Serialization) Person Json = ${_jPerson}")
(Serialization) Person Json = {"id":2,"name":"Droid","position":"Staff","times":"12:2:0"}
スポンサーリンク

関連記事:

Kotlinで利用可能なJSONライブラリーには「GSON, Jackson, Moshi, Kotlin serializationなど」があります。 始めの3つはJavaがベースです。その中のMoshiは、拡張機能によりKotlinとの親和性が高められています。 Kotlin serializationはJSON以外(Protobuf, CBOR, Hocon, Properties )のフォーマットも扱えます。フォーマットを扱うというよりも、シリアル化の機能を重視したライブラリーのようです。 プログラム間でデータを受け渡す際に用いるのであれば、Kotlin serializationが最も適しているかも知れません。 Kotlin serializationに興味を引かれますが、後の機会に置いといて... 今回は、Moshiについて、まとめます。 ※環境:Android Studio Ladybug Feature Drop | 2024.2.2     Kotlin 2.0.0     Moshi 1.15.2 ...
MoshiでJSONをパース(JSONの記述⇔データクラスのオブジェクト)する場合に、一般クラスは未対応です。 例えば、データクラス以外の、ユーザ定義のクラスはパース出来ません。 対応させるためには、そのクラスのカスタムJsonAdapterを作成します。 そして、相互変換する方法をプログラマー側で定義します。 カスタムJsonAdapterの作成方法をまとめます。 ※環境:Android Studio Ladybug Feature Drop | 2024.2.2     Kotlin 2.0.0     Moshi 1.15.2 ...
RetrofitはRESTに準拠したWeb APIです。 このRetrofitとMoshiを使って、Webサービスへアクセスする方法を、まとめました。 サンプルはAndroidのコードラボと同じ内容です。私はコードラボが理解し難かったので、不要な部分をそぎ落として、Retrofitに的を絞って説明しています。 ※環境:Android Studio Ladybug Feature Drop | 2024.2.2     Kotlin 2.0.0     Moshi 1.15.2     Retrofit2 2.11.0     Coil 3.1.0 ...
Kotlinで利用可能なJSONライブラリーには「GSON, Jackson, Moshi, Kotlin serializationなど」があります。 Kotlin Serializationは、Kotlinがベース(前の3つはJavaがベース)です。 2020.10にVer 1.0.0がリリースされており、比較的新しいライブラリです。 今回は、Kotlin serializationについて、まとめます。 ※環境:Android Studio Ladybug Feature Drop | 2024.2.2 Patch 1     Kotlin 2.0.0     Kotlin serialization json 1.7.1 ...
Kotlin serializationでJSONをパース(JSONの記述⇔クラスのオブジェクト)する場合に、一般クラスは未対応です。 例えば、データクラス(コンストラクタの引数でプロパティを指定するクラス)以外の、ユーザ定義のクラスはパース出来ません。また、ライブラリ提供のクラスもパース出来ません。 対応させるためには、そのクラスのカスタムSerializerを作成します。 そして、相互変換する方法をプログラマー側で定義します。 カスタムSerializer(ライブラリ提供クラス、Primitive版)の作成方法をまとめます。 ※環境:Android Studio Meerkat | 2024.3.1     Kotlin 2.0.0     Kotlin serialization json 1.7.1 ...
Kotlin serializationでJSONをパース(JSONの記述⇔クラスのオブジェクト)する場合に、一般クラスは未対応です。 例えば、データクラス(コンストラクタの引数でプロパティを指定するクラス)以外の、ユーザ定義のクラスはパース出来ません。また、ライブラリ提供のクラスもパース出来ません。 対応させるためには、そのクラスのカスタムSerializerを作成します。 そして、相互変換する方法をプログラマー側で定義します。 カスタムSerializer(ユーザ定義クラス、Delegating版)の作成方法をまとめます。 ※環境:Android Studio Meerkat | 2024.3.1     Kotlin 2.0.0     Kotlin serialization json 1.7.1 ...
Kotlin serializationでJSONをパース(JSONの記述⇔クラスのオブジェクト)する場合に、一般クラスは未対応です。 例えば、データクラス(コンストラクタの引数でプロパティを指定するクラス)以外の、ユーザ定義のクラスはパース出来ません。また、ライブラリ提供のクラスもパース出来ません。 対応させるためには、そのクラスのカスタムSerializerを作成します。 そして、相互変換する方法をプログラマー側で定義します。 カスタムSerializer(ライブラリ提供クラス、Surrogate版)の作成方法をまとめます。 ※環境:Android Studio Meerkat | 2024.3.1     Kotlin 2.0.0     Kotlin serialization json 1.7.1 ...
Kotlin serializationでJSONをパース(JSONの記述⇔クラスのオブジェクト)する場合に、一般クラスは未対応です。 例えば、データクラス(コンストラクタの引数でプロパティを指定するクラス)以外の、ユーザ定義のクラスはパース出来ません。また、ライブラリ提供のクラスもパース出来ません。 対応させるためには、そのクラスのカスタムSerializerを作成します。 そして、相互変換する方法をプログラマー側で定義します。 カスタムSerializer(ユーザ定義クラス、Handwritten版)の作成方法をまとめます。 ※環境:Android Studio Meerkat | 2024.3.1     Kotlin 2.0.0     Kotlin serialization json 1.7.1 ...
スポンサーリンク