Kotlin serializationでJSONをパース

投稿日:  更新日:

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とは

Kotlin serializationは、Kotlin上で動作するJSONライブラリーです。

Kotlin serializationを使うと、「JSONの記述⇔クラスのオブジェクト」といった相互変換(パース:解析して変換)が簡単に出来ます。

Kotlin serializationに出来ること

プログラム間(コンピュータ間や異なるプログラミング言語間など)でデータを受け渡す際に、データのフォーマットとしてJSONが広く使われています。

その一例は、Retrofit(REST準拠のAPI)です。

RetrofitでKotlin Serializationを利用すれば、HTTPプロトコルでサーバーにデータを要求し、返答をJSONフォーマットで受け取り、クラスのオブジェクトでプログラムへ取り込むことが可能です。

Kotlin Serializationの使用事例(Retrofitの場合)

さらに、Kotlin serializationはJSON以外のフォーマットも扱えます。
※フォーマットについては「Serialization formats」を参照

Kotlin Serializationが扱うフォーマット
HOCON(Human-Optimized Config Object Notation)
ProtoBuf(Protocol Buffers )
CBOR(Concise Binary Object Representation)
※Properties(Java Properties)

Kotlin serializationはJSONに特化していないことから、フォーマットを扱うというよりも、シリアル化の機能を重視したライブラリーのようです。
※シリアル化については「シリアル化(serialize)の意味と役割」を参照

スポンサーリンク

環境設定

「app/build.gradle」へ次の設定を行います。

plugins {
    ...
    id("org.jetbrains.kotlin.plugin.serialization") version "2.0.0" // Ktolinのバージョンに合わせる
//    id("org.jetbrains.kotlin.plugin.serialization") version "2.0.20"
//    id("org.jetbrains.kotlin.plugin.serialization") version "2.1.0"
}

android { ... }

dependencies {
    ...
    implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.7.1")
//    implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.7.3")
//    implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.8.0")
}

Serializationプラグイン

Kotlinのリリースに合わせて、Serializationプラグインもリリースされます。

両者のバージョンは合わせるようにします。

プラグインのバージョンについてはリリースノートを参照して下さい。

プラグインのバージョン

スポンサーリンク

Serializationライブラリ

Kotlinのバージョンに対応するSerializationライブラリーがあります。

対応するライブラリーを使った方が良さそうです。※ドキュメントに指示は無かったと思いますが…

ライブラリーのバージョンについてはリリースノートを参照して下さい。

ライブラリーのバージョン

スポンサーリンク

基本的なSerializationの動作(例:プリミティブ型)

Android Studioはビルドをする際に、@Serializable付きのデータクラスを認識して、データクラスのJavaバイナリへ、実装済みSerializerクラスを埋め込みます。

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

Kotlin Serializationのビルドフロー

Serializerクラス(KSerializerインターフェース)は、serialize( )とdeserialize( )関数を持ちます。両者は相互変化で呼び出される関数です。変換の方法が定義されています。

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()
    }
}
実装済みSerializerクラスの例(Personの場合、デコンパイル結果)
public final class Person {
    @NotNull
    public static final Companion Companion = new Companion(null);
    private final int id;
    @NotNull
    private final String name;
    @NotNull
    private final String post;
    public static final int $stable = 0;

    ...
	
    public static final class Companion {
        private Companion() {
        }

        public /* synthetic */ Companion(DefaultConstructorMarker $constructor_marker) {
            this();
        }

        @NotNull
        public final KSerializer<Person> serializer() {
            return Person$$serializer.INSTANCE;
        }
    }

    ...
}
public /* synthetic */ class Person$$serializer implements GeneratedSerializer<Person> {
    @NotNull
    private static final SerialDescriptor descriptor;
    @NotNull
    public static final Person$$serializer INSTANCE = new Person$$serializer();
    public static final int $stable = 8;

    private Person$$serializer() {
    }

    @NotNull
    public final SerialDescriptor getDescriptor() {
        return descriptor;
    }

    public final void serialize(@NotNull Encoder encoder, @NotNull Person value) {
        Intrinsics.checkNotNullParameter(encoder, "encoder");
        Intrinsics.checkNotNullParameter(value, "value");
        SerialDescriptor serialDescriptor = descriptor;
        CompositeEncoder beginStructure = encoder.beginStructure(serialDescriptor);
        Person.write$Self$test_serialization_debug(value, beginStructure, serialDescriptor);
        beginStructure.endStructure(serialDescriptor);
    }

    @NotNull
    public final Person deserialize(@NotNull Decoder decoder) {
        Intrinsics.checkNotNullParameter(decoder, "decoder");
        SerialDescriptor serialDescriptor = descriptor;
        boolean z = true;
        int i = 0;
        int i2 = 0;
        String str = null;
        String str2 = null;
        CompositeDecoder beginStructure = decoder.beginStructure(serialDescriptor);
        if (beginStructure.decodeSequentially()) {
            i2 = beginStructure.decodeIntElement(serialDescriptor, 0);
            str = beginStructure.decodeStringElement(serialDescriptor, 1);
            str2 = beginStructure.decodeStringElement(serialDescriptor, 2);
            i = 0 | 1 | 2 | 4;
        } else {
            while (z) {
                int decodeElementIndex = beginStructure.decodeElementIndex(serialDescriptor);
                switch (decodeElementIndex) {
                    case -1:
                        z = false;
                        break;
                    case 0:
                        i2 = beginStructure.decodeIntElement(serialDescriptor, 0);
                        i |= 1;
                        break;
                    case 1:
                        str = beginStructure.decodeStringElement(serialDescriptor, 1);
                        i |= 2;
                        break;
                    case 2:
                        str2 = beginStructure.decodeStringElement(serialDescriptor, 2);
                        i |= 4;
                        break;
                    default:
                        throw new UnknownFieldException(decodeElementIndex);
                }
            }
        }
        beginStructure.endStructure(serialDescriptor);
        return new Person(i, i2, str, str2, null);
    }

    ...
}
スポンサーリンク

JSON⇒Obj変換

Json#decodeFromStringを呼び出します。

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

    val _jPerson = """
            {
                "id":1,
                "name":"Android",
                "position":"Manager"
            }
            """.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)

Obj⇒JSON変換

Json#encodeToStringを呼び出します。

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

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

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

Jsonはユーティリティークラス

Jsonクラスは自身を継承したコンパニオンオブジェクト(クラス内オブジェクト)を持ちます。

ですので、Json.encodeToString( )という形式で実行すると、Jsonオブジェクトはシングルトンになり、Jsonクラスの関数はコンパニオンオブジェクトの関数になります。

そして、Jsonクラスの関数はユーティリティークラスの関数と同じ扱いになります。

public sealed class Json(
    public val configuration: JsonConfiguration,
    override val serializersModule: SerializersModule
) : StringFormat {

    ...
    @ThreadLocal // to support caching on K/N
    @OptIn(ExperimentalSerializationApi::class)
    public companion object Default : Json(JsonConfiguration(), EmptySerializersModule())

    ...
    public final override fun <T> encodeToString(serializer: SerializationStrategy<T>, value: T): String {
        val result = JsonToStringWriter()
        try {
            encodeByWriter(this@Json, result, serializer, value)
            return result.toString()
        } finally {
            result.release()
        }
    }

    ...
    public inline fun <reified T> decodeFromString(@FormatLanguage("json", "", "") string: String): T =
            decodeFromString(serializersModule.serializer(), string)

    ...
    public final override fun <T> decodeFromString(deserializer: DeserializationStrategy<T>, @FormatLanguage("json", "", "") string: String): T {
        val lexer = StringJsonLexer(this, string)
        val input = StreamingJsonDecoder(this, WriteMode.OBJ, lexer, deserializer.descriptor, null)
        val result = input.decodeSerializableValue(deserializer)
        lexer.expectEof()
        return result
    }
    
	...
}
スポンサーリンク

例:Array

Array型のプロパティを持つ場合の相互変換です。

@Serializable
data class Person(
    val id: Int,
    val name: String,
    @SerialName("position") val post: String,  // JSONのキーに別名を定義
    val times: Array<Int>
)
 JSON⇒Obj変換 
    val _jPerson = """
            {
                "id":1,
                "name":"Android",
                "position":"Manager",
                "times":[32122, 43290, 46662, 68756]
            }
            """.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=[32122, 43290, 46662, 68756])
 Obj⇒JSON変換 
    val _oPerson = Person(
        id = 2, name = "Droid", post = "Staff",
        times = arrayOf(28207, 43320, 46750, 62159)
    )
    val _jPerson = Json.encodeToString(_oPerson)

    Log.i(TAG, "(Serialization) Person Json = ${_jPerson}")
(Serialization) Person Json = {"id":2,"name":"Droid","position":"Staff","times":[28207,43320,46750,62159]}
スポンサーリンク

例:List

List型のプロパティを持つ場合の相互変換です。

@Serializable
data class Person(
    val id: Int,
    val name: String,
    @SerialName("position") val post: String,  // JSONのキーに別名を定義
    val times: List<Int>
)
 JSON⇒Obj変換 
    val _jPerson = """
            {
                "id":1,
                "name":"Android",
                "position":"Manager",
                "times":[32122, 43290, 46662, 68756]
            }
            """.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=[32122, 43290, 46662, 68756])
 Obj⇒JSON変換 
    val _oPerson = Person3(
        id = 2, name = "Droid", post = "Staff",
        times = listOf(28207, 43320, 46750, 62159)
    )
    val _jPerson = Json.encodeToString(_oPerson)

    Log.i(TAG, "(Serialization) Person Json = ${_jPerson}")
(Serialization) Person Json = {"id":2,"name":"Droid","position":"Staff","times":[28207,43320,46750,62159]}
スポンサーリンク

例:Map

Map型のプロパティを持つ場合の相互変換です。

@Serializable
data class Person(
    val id: Int,
    val name: String,
    @SerialName("position") val post: String,  // JSONのキーに別名を定義
    val times: Map<Int, String>
)
 JSON⇒Obj変換 
    val _jPerson = """
            {
                "id":1,
                "name":"Android",
                "position":"Manager",
                "times":{"32122":"login","43290":"pause", "46662":"resume", "68756":"logout"}
            }
            """.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={32122=login, 43290=pause, 46662=resume, 68756=logout})
 Obj⇒JSON変換 
    val _oPerson = Person(
        id = 2, name = "Droid", post = "Staff",
        times = mapOf(28207 to "login", 43320 to "pause", 46750 to "resume", 62159 to "login")
    )
    val _jPerson = Json.encodeToString(_oPerson)

    Log.i(TAG, "(Serialization) Person Json = ${_jPerson}")
(Serialization) Person Json = {"id":2,"name":"Droid","position":"Staff","times":{"28207":"login","43320":"pause","46750":"resume","62159":"login"}}
スポンサーリンク

例:Set

Set型のプロパティを持つ場合の相互変換です。

@Serializable
data class Person(
    val id: Int,
    val name: String,
    @SerialName("position") val post: String,  // JSONのキーに別名を定義
    val times: Set<Int>
)
 JSON⇒Obj変換 
    val _jPerson = """
            {
                "id":1,
                "name":"Android",
                "position":"Manager",
                "times":[32122, 43290, 46662, 68756]
            }
            """.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=[32122, 43290, 46662, 68756])
 Objス⇒JSON変換 
    val _oPerson = Person(
        id = 2, name = "Droid", post = "Staff",
        times = setOf(28207, 43320, 46750, 62159)
    )
    val _jPerson = Json.encodeToString(_oPerson)

    Log.i(TAG, "(Serialization) Person Json = ${_jPerson}")
(Serialization) Person Json = {"id":2,"name":"Droid","position":"Staff","times":[28207,43320,46750,62159]}
スポンサーリンク

例:Enum

Enum型のプロパティを持つ場合の相互変換です。

enum class TimeSignal { LOGIN, LOGOUT, PAUSE, RESUME }

@Serializable
data class Person(
    val id: Int,
    val name: String,
    @SerialName("position") val post: String,  // JSONのキーに別名を定義
    val times: TimeSignal
)
 JSON⇒Obj変換 
    val _jPerson = """
            {
                "id":1,
                "name":"Android",
                "position":"Manager",
                "times":"LOGIN"
            }
            """.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=LOGIN)
 Obj⇒JSON変換 
    val _oPerson = Person(id = 2, name = "Droid", post = "Staff", times = TimeSignal.LOGOUT)
    val _jPerson = Json.encodeToString(_oPerson)

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

例:データクラス

データクラス型のプロパティを持つ場合の相互変換です。

@Serializable
data class TimeStamp(val hour: Int, val min: Int, val sec: Int)

@Serializable
data class Person(
    val id: Int,
    val name: String,
    @SerialName("position") val post: String,  // JSONのキーに別名を定義
    val times: TimeStamp
)
 JSON⇒Obj変換 
    val _jPerson = """
            {
                "id":1,
                "name":"Android",
                "position":"Manager",
                "times":{"hour":8,"min":55,"sec":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=TimeStamp(hour=8, min=55, sec=22))
 Obj⇒JSON変換 
    val _oPerson = Person(
        id = 2, name = "Droid", post = "Staff",
        times = TimeStamp(7, 50, 7)
    )
    val _jPerson = Json.encodeToString(_oPerson)

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

nullable/non-nullとデフォルト値の対応

Kotolin serializationにおいて、nullable/non-nullとデフォルト値の対応は表のようになります。

JSONでkey:valueが無い(JSON⇒Obj変換)

JSONの記述でKey : Valueが欠けることは許されません。欠けているとExceptionになります。

ただし、デフォルト値が設定されていれば、デフォルト値で補われます。

JSONの記述データクラスの定義オブジェクト
{
"id":1,
"name":"Android"
}


※key : valueが無い

(変換)
data class Person(
 val id: Int,
 val name: String,
 val post: String
)
MissingFieldException
data class Person(
 val id: Int,
 val name: String,
 val post: String?
)
data class Person(
 val id: Int,
 val name: String,
 val post: String = ”Staff”
)
id = 1
name = Android
post = Staff
data class Person(
 val id: Int,
 val name: String,
 val post: String? = null
)
id = 1
name = Android
post = null
kotlinx.serialization.MissingFieldException: 
    Field 'post' is required for type with serial name 'Person',
	but it was missing at path
スポンサーリンク

JSONでvalueがnull(JSON⇒Obj変換)

JSONの記述でvalueがnullの場合は、プロパティはnullable型が必須です。non-null型になっているとExceptionになります。

JSONの記述データクラスの定義オブジェクト
{
"id":1,
"name":"Android",
"post":null
}


※valueがnull

(変換)
data class Person(
 val id: Int,
 val name: String,
 val post: String
)
JsonDecodingException
data class Person(
 val id: Int,
 val name: String,
 val post: String?
)
id = 1
name = Android
post = null
data class Person(
 val id: Int,
 val name: String,
 val post: String = ”Staff”
)
JsonDecodingException
※1
data class Person(
 val id: Int,
 val name: String,
 val post: String? = null
)
id = 1
name = Android
post = null
※1:「coerceInputValues = true」で回避可能
kotlinx.serialization.json.internal.JsonDecodingException: 
    Unexpected JSON token at offset 47: Expected string literal but 'null' literal was found at path.
Use 'coerceInputValues = true' in 'Json {}' builder to coerce nulls if property has a default value.
JSON input: {
    "id":1,
    "name":"Android",
    "post":null
}

プロパティがnull(Obj⇒JSON変換)

プロパティがnullの場合は、JSONの記述にそのままnullが出力されます。

データクラスの定義オブジェクトJSONの記述
data class Person(
 val id: Int,
 val name: String,
 val post: String?
)
id = 2
name = Droid
post = null

(変換)
{
"id":2,
"name":"Droid"
"post":null
}
スポンサーリンク

関連記事:

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 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(ライブラリ提供クラス、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 ...
RetrofitはRESTに準拠したWeb APIです。 このRetrofitとKotlin Serializationを使って、Webサービスへアクセスする方法を、まとめました。 サンプルはAndroidのコードラボと同じ内容です。私はコードラボが理解し難かったので、不要な部分をそぎ落として、Retrofitに的を絞って説明しています。 ※環境:Android Studio Meerkat | 2024.3.1     Kotlin 2.0.0     Kotlin serialization json 1.7.1     Retrofit2 2.11.0     Coil 3.1.0 ...
スポンサーリンク