アプリ開発備忘録

PlayStationMobile、Android、UWPの開発備忘録

Enumを扱う【kotlinx.serialization】

Enumもカスタムタイプのパースと同じく、 Serializer を作成しなければなりません。Jacksonは @JsonCreator@JsonValue だけでできるので、少し面倒に感じましたが、メリットはあります。

Stringの値でパースする abstract なシリアライザを作成しました。

internal interface StringEnum {
    public val label: String?

    companion object {
        internal abstract class Serializer<T : Enum<T>>(private val kClass: KClass<T>) : KSerializer<T?> {

            override val descriptor: SerialDescriptor =
                PrimitiveSerialDescriptor(this::class.jvmName, PrimitiveKind.STRING)

            override fun serialize(encoder: Encoder, value: T?) {
                when (val label = (value as StringEnum).label) {
                    null -> encoder.encodeNull()
                    else -> encoder.encodeString(label)
                }
            }

            override fun deserialize(decoder: Decoder): T? {
                val value = decoder.decodeString()
                return kClass.java.enumConstants
                    .firstOrNull { (it as StringEnum).label == value }
            }
        }
    }
}

このように定義します。

@Serializable(Type.Companion.Serializer::class)
enum class Type() : StringEnum {
    ONE {
        override val label: String = "one"
    },

    TWO {
        override val label: String = "two"
    },
    ;

    companion object {
        object Serializer : StringEnum.Companion.Serializer<Type>(Type::class)
    }
}

このように使用します。

@Serializable
internal data class Root(
    @SerialName("type") val type: Type?
)

nullableにしないとコンパイル時にエラーが出るのでそこは安全です。サーバーで新しい値が定義された時はパースの失敗ではなく、nullになるので安全です。
f:id:matsudamper:20210217071231p:plain

バージョン情報

plugins {
    kotlin("jvm") version "1.4.30"
    kotlin("plugin.serialization") version "1.4.10"
}

dependencies {
    implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.0.1")
}