From 310ebbaf61d4631f2078a85e123805a09e0e8c0a Mon Sep 17 00:00:00 2001 From: Kristjan Kotnik Date: Tue, 5 Nov 2024 11:34:44 +0100 Subject: [PATCH] feat(retrofit): Add JsonFallback annotation and addEnumAdapter extension function for adding fallback to enums --- config/libs.toml | 1 + retrofit/README.MD | 22 +++++++ retrofit/build.gradle.kts | 1 + .../kotlinova/retrofit/moshi/JsonFallback.kt | 65 +++++++++++++++++++ 4 files changed, 89 insertions(+) create mode 100644 retrofit/src/jvmCommon/kotlin/si/inova/kotlinova/retrofit/moshi/JsonFallback.kt diff --git a/config/libs.toml b/config/libs.toml index 1a868456..7c82a3c4 100644 --- a/config/libs.toml +++ b/config/libs.toml @@ -83,6 +83,7 @@ junit4 = { module = "junit:junit", version.ref = "junit4" } junit5-api = { module = "org.junit.jupiter:junit-jupiter-engine", version.ref = "junit5" } junit5-engine = { module = "org.junit.jupiter:junit-jupiter-engine", version.ref = "junit5" } moshi = { module = "com.squareup.moshi:moshi", version.ref = "moshi" } +moshi-adapters = { module = "com.squareup.moshi:moshi-adapters", version.ref = "moshi" } okhttp = { module = "com.squareup.okhttp3:okhttp", version.ref = "okhttp" } okhttp-mockWebServer = { module = "com.squareup.okhttp3:mockwebserver", version.ref = "okhttp" } orgJson = { module = "org.json:json", version.ref = "orgJson" } diff --git a/retrofit/README.MD b/retrofit/README.MD index 1edb79a3..19c20526 100644 --- a/retrofit/README.MD +++ b/retrofit/README.MD @@ -116,6 +116,28 @@ moshiBuilder.add(JavaTimeMoshiAdapter) Moshi adapter for converting java.time's `Instant`, `LocalTime`, `LocalDate`, `LocalDateTime` and `ZonedDateTime` to ISO String for JSON serialization. +## Enum json fallback + +Annotation `@JsonFallback` for adding fallback value to enum. +Enum class can be added via `Moshi.Builder` with extension function `addEnumAdapter`. + +```kotlin +@JsonClass(generateAdapter = false) +enum class Level { + @Json(name = "low") + LOW, + + @Json(name = "medium") + MEDIUM, + + @Json(name = "high") + HIGH, + + @JsonFallback + OTHER; +} +``` + ## OKHttp Cache manager (Only on Android projects) diff --git a/retrofit/build.gradle.kts b/retrofit/build.gradle.kts index 26e3d2ae..3977811d 100644 --- a/retrofit/build.gradle.kts +++ b/retrofit/build.gradle.kts @@ -44,6 +44,7 @@ kotlin { dependencies { api(libs.okhttp) api(libs.moshi) + api(libs.moshi.adapters) api(libs.retrofit) implementation(projects.core) diff --git a/retrofit/src/jvmCommon/kotlin/si/inova/kotlinova/retrofit/moshi/JsonFallback.kt b/retrofit/src/jvmCommon/kotlin/si/inova/kotlinova/retrofit/moshi/JsonFallback.kt new file mode 100644 index 00000000..e9761549 --- /dev/null +++ b/retrofit/src/jvmCommon/kotlin/si/inova/kotlinova/retrofit/moshi/JsonFallback.kt @@ -0,0 +1,65 @@ +/* + * Copyright 2023 INOVA IT d.o.o. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software + * is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package si.inova.kotlinova.retrofit.moshi + +import com.squareup.moshi.JsonAdapter +import com.squareup.moshi.Moshi +import com.squareup.moshi.adapters.EnumJsonAdapter + +/** + * Annotation for adding fallback value to enum. + * + * Enum class can be added via [Moshi.Builder] with extension function [addEnumAdapter]. + * + * Usage: + * + * ```kotlin + * @JsonClass(generateAdapter = false) + * enum class Level { + * @Json(name = "low") + * LOW, + * + * @Json(name = "medium") + * MEDIUM, + * + * @Json(name = "high") + * HIGH, + * + * @JsonFallback + * OTHER; + * } + * ``` + */ +annotation class JsonFallback + +inline fun > Moshi.Builder.addEnumAdapter(): Moshi.Builder = + with(T::class.java) { add(this, createEnumAdapter()) } + +fun > Class.createEnumAdapter(): JsonAdapter { + var default: T? = null + + enumConstants?.forEach { enumConstant -> + if (getField(enumConstant.name).isAnnotationPresent(JsonFallback::class.java)) { + check(default == null) { "Multiple entries annotated with @${JsonFallback::class.simpleName} on $name" } + default = enumConstant + } + } + + requireNotNull(default) { "No entry annotated with @${JsonFallback::class.simpleName} on $name" } + + return EnumJsonAdapter.create(this).withUnknownFallback(default) +}