Skip to content

Commit

Permalink
Add support for Gson serialization
Browse files Browse the repository at this point in the history
  • Loading branch information
azabost committed Jun 29, 2023
1 parent 81fb8f1 commit 6f10657
Show file tree
Hide file tree
Showing 11 changed files with 189 additions and 0 deletions.
1 change: 1 addition & 0 deletions buildSrc/src/main/kotlin/Dependencies.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@ object Dependencies {
const val serializationCore = "org.jetbrains.kotlinx:kotlinx-serialization-core:${Versions.serialization}"
const val serializationJson = "org.jetbrains.kotlinx:kotlinx-serialization-json:${Versions.serialization}"
const val jackson = "com.fasterxml.jackson.core:jackson-databind:${Versions.jackson}"
const val gson = "com.google.code.gson:gson:${Versions.gson}"
}
1 change: 1 addition & 0 deletions buildSrc/src/main/kotlin/Versions.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@ object Versions {
const val shouldko = "0.2.2"
const val serialization = "1.5.1"
const val jackson = "2.15.2"
const val gson = "2.10.1"
}
1 change: 1 addition & 0 deletions enums-gson/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/build
16 changes: 16 additions & 0 deletions enums-gson/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
plugins {
kotlin("jvm")
id("default-config")
id("default-java-publish")
}

dependencies {
api(rootProject)
api(project(":enums"))

implementation(kotlin("stdlib"))
implementation(Dependencies.gson)

testImplementation(Dependencies.junit)
testImplementation(Dependencies.shouldko)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package pl.brightinventions.codified.gson

import com.google.gson.Gson
import com.google.gson.TypeAdapter
import com.google.gson.TypeAdapterFactory
import com.google.gson.reflect.TypeToken
import com.google.gson.stream.JsonReader
import com.google.gson.stream.JsonWriter
import pl.brightinventions.codified.Codified
import pl.brightinventions.codified.enums.CodifiedEnum
import pl.brightinventions.codified.enums.CodifiedEnumDecoder
import java.lang.reflect.ParameterizedType

class CodifiedEnumTypeAdapter<T, C : Any>(
enumTypeToken: TypeToken<*>,
private val codeAdapter: TypeAdapter<C>,
) : TypeAdapter<CodifiedEnum<T, C>>() where T : Enum<T>, T : Codified<C> {

@Suppress("UNCHECKED_CAST")
private val enumClass = enumTypeToken.rawType as Class<T>

class Factory : TypeAdapterFactory {
override fun <T : Any> create(gson: Gson, typeToken: TypeToken<T>?): TypeAdapter<T>? {
if (typeToken?.rawType != CodifiedEnum::class.java) {
return null
}

val parameterizedType = typeToken.type as ParameterizedType
val enumType = parameterizedType.actualTypeArguments[0]
val enumTypeToken = TypeToken.get(enumType)
val codeType = parameterizedType.actualTypeArguments[1]
val codeAdapter = gson.getAdapter(TypeToken.get(codeType))

@Suppress("UNCHECKED_CAST")
return CodifiedEnumTypeAdapter(enumTypeToken, codeAdapter) as TypeAdapter<T>
}
}

override fun write(out: JsonWriter, value: CodifiedEnum<T, C>?) {
if (value != null) {
codeAdapter.write(out, value.code())
} else {
out.nullValue()
}
}

override fun read(`in`: JsonReader): CodifiedEnum<T, C> {
val code = codeAdapter.read(`in`)
return CodifiedEnumDecoder.decode(code, enumClass)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
package pl.brightinventions.codified.gson

import com.google.gson.GsonBuilder
import org.junit.jupiter.api.Test
import pl.brightinventions.codified.enums.CodifiedEnum
import pl.brightinventions.codified.enums.codifiedEnum
import pl.miensol.shouldko.shouldEqual


class GsonTest {

private val gson = GsonBuilder()
.registerTypeAdapterFactory(CodifiedEnumTypeAdapter.Factory())
.create()


@Test
fun `mixed enum wrapper with known values should be serialized`() {
val wrapper = MixedEnumWrapper(StringEnum.FOO.codifiedEnum(), IntEnum.QUX.codifiedEnum())

val string = gson.toJson(wrapper)

string.shouldEqual("{\"stringEnum\":\"${StringEnum.FOO.code}\",\"intEnum\":${IntEnum.QUX.code}}")
}

@Test
fun `mixed enum wrapper with known values should be deserialized`() {
val string = "{\"stringEnum\":\"${StringEnum.FOO.code}\",\"intEnum\":${IntEnum.QUX.code}}"

val wrapper = gson.fromJson(string, MixedEnumWrapper::class.java)

wrapper.stringEnum.knownOrNull().shouldEqual(StringEnum.FOO)
wrapper.intEnum.knownOrNull().shouldEqual(IntEnum.QUX)
}

@Test
fun `mixed enum wrapper with unknown values should be serialized`() {
val wrapper = MixedEnumWrapper(CodifiedEnum.Unknown("hello"), CodifiedEnum.Unknown(123))

val string = gson.toJson(wrapper)

string.shouldEqual("{\"stringEnum\":\"hello\",\"intEnum\":123}")
}

@Test
fun `mixed enum wrapper with unknown values should be deserialized`() {
val string = "{\"stringEnum\":\"hello\",\"intEnum\":123}"

val wrapper = gson.fromJson(string, MixedEnumWrapper::class.java)

wrapper.stringEnum.code().shouldEqual("hello")
wrapper.intEnum.code().shouldEqual(123)
}

@Test
fun `known and unknown enums in a wrapper should be serialized`() {
val enums = listOf<CodifiedEnum<StringEnum, String>>(
CodifiedEnum.Known(StringEnum.BAR),
CodifiedEnum.Unknown("hello"),
CodifiedEnum.Known(StringEnum.FOO),
CodifiedEnum.Unknown("world"),
)

val wrapper = StringEnumListWrapper(enums)

val string = gson.toJson(wrapper)

string.shouldEqual("{\"stringEnums\":[\"rab\",\"hello\",\"oof\",\"world\"]}")
}

@Test
fun `known and unknown enums in a wrapper should be deserialized`() {
val string = "{\"stringEnums\":[\"rab\",\"hello\",\"oof\",\"world\"]}"

val wrapper = gson.fromJson(string, StringEnumListWrapper::class.java)

wrapper.stringEnums.size.shouldEqual(4)
wrapper.stringEnums[0].knownOrNull().shouldEqual(StringEnum.BAR)
wrapper.stringEnums[1].code().shouldEqual("hello")
wrapper.stringEnums[2].knownOrNull().shouldEqual(StringEnum.FOO)
wrapper.stringEnums[3].code().shouldEqual("world")
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package pl.brightinventions.codified.gson

import pl.brightinventions.codified.Codified

const val BAZ_CODE = 100
const val QUX_CODE = 999

enum class IntEnum(override val code: Int) : Codified<Int> {
BAZ(BAZ_CODE), QUX(QUX_CODE);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package pl.brightinventions.codified.gson

import pl.brightinventions.codified.enums.CodifiedEnum

data class MixedEnumWrapper(
val stringEnum: CodifiedEnum<StringEnum, String>,
val intEnum: CodifiedEnum<IntEnum, Int>,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package pl.brightinventions.codified.gson

import pl.brightinventions.codified.Codified

const val FOO_CODE = "oof"
const val BAR_CODE = "rab"

enum class StringEnum(override val code: String) : Codified<String> {
FOO(FOO_CODE), BAR(BAR_CODE);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package pl.brightinventions.codified.gson

import pl.brightinventions.codified.enums.CodifiedEnum

data class StringEnumListWrapper(
val stringEnums: List<CodifiedEnum<StringEnum, String>>
)
1 change: 1 addition & 0 deletions settings.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ rootProject.name = "codified"
include("enums")
include("enums-serializer")
include("enums-jackson")
include("enums-gson")

0 comments on commit 6f10657

Please sign in to comment.