-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add jackson module for serialization and deserialization (#3)
Add jackson module for serialization and deserialization
- Loading branch information
Showing
13 changed files
with
374 additions
and
8 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
/build |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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.jackson) | ||
|
||
testImplementation(Dependencies.junit) | ||
testImplementation(Dependencies.shouldko) | ||
} |
39 changes: 39 additions & 0 deletions
39
enums-jackson/src/main/kotlin/pl/brightinventions/codified/jackson/CodifiedDeserializer.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
package pl.brightinventions.codified.jackson | ||
|
||
import com.fasterxml.jackson.core.JsonParser | ||
import com.fasterxml.jackson.databind.BeanProperty | ||
import com.fasterxml.jackson.databind.DeserializationContext | ||
import com.fasterxml.jackson.databind.JavaType | ||
import com.fasterxml.jackson.databind.JsonDeserializer | ||
import com.fasterxml.jackson.databind.deser.ContextualDeserializer | ||
import pl.brightinventions.codified.Codified | ||
import pl.brightinventions.codified.enums.CodifiedEnumDecoder | ||
import java.lang.reflect.ParameterizedType | ||
|
||
class CodifiedDeserializer<TEnum, TCode : Any>() : JsonDeserializer<TEnum>(), | ||
ContextualDeserializer where TEnum : Codified<TCode>, TEnum : Enum<TEnum> { | ||
private lateinit var enumCodeType: Class<TCode> | ||
private lateinit var enumType: Class<TEnum> | ||
|
||
override fun deserialize(p: JsonParser, ctxt: DeserializationContext?): TEnum? { | ||
@Suppress("UNCHECKED_CAST") | ||
return CodifiedEnumDecoder.decode(p.readValueAs(enumCodeType), enumType).knownOrNull() | ||
} | ||
|
||
override fun createContextual( | ||
ctxt: DeserializationContext, | ||
property: BeanProperty? | ||
): JsonDeserializer<TEnum> { | ||
return CodifiedDeserializer(ctxt.contextualType) | ||
} | ||
|
||
override fun handledType(): Class<*> { | ||
return Codified::class.java | ||
} | ||
|
||
@Suppress("UNCHECKED_CAST") | ||
constructor(contextualType: JavaType) : this() { | ||
this.enumType = contextualType.rawClass as Class<TEnum> | ||
this.enumCodeType = (enumType.genericInterfaces[0] as ParameterizedType).actualTypeArguments[0] as Class<TCode> | ||
} | ||
} |
36 changes: 36 additions & 0 deletions
36
...-jackson/src/main/kotlin/pl/brightinventions/codified/jackson/CodifiedEnumDeserializer.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
package pl.brightinventions.codified.jackson | ||
|
||
import com.fasterxml.jackson.core.JsonParser | ||
import com.fasterxml.jackson.databind.BeanProperty | ||
import com.fasterxml.jackson.databind.DeserializationContext | ||
import com.fasterxml.jackson.databind.JavaType | ||
import com.fasterxml.jackson.databind.JsonDeserializer | ||
import com.fasterxml.jackson.databind.deser.ContextualDeserializer | ||
import pl.brightinventions.codified.Codified | ||
import pl.brightinventions.codified.enums.CodifiedEnum | ||
import pl.brightinventions.codified.enums.CodifiedEnumDecoder | ||
import java.io.Serializable | ||
|
||
internal class CodifiedEnumDeserializer<TEnum, TCode : Any>() : JsonDeserializer<CodifiedEnum<TEnum, TCode>>(), | ||
ContextualDeserializer where TEnum : Codified<TCode>, TEnum : Enum<TEnum> { | ||
private lateinit var enumCodeType: Class<TCode> | ||
private lateinit var enumType: Class<TEnum> | ||
|
||
override fun deserialize(p: JsonParser, ctxt: DeserializationContext?): CodifiedEnum<TEnum, TCode> { | ||
@Suppress("UNCHECKED_CAST") | ||
return CodifiedEnumDecoder.decode(p.readValueAs(enumCodeType), enumType) | ||
} | ||
|
||
override fun createContextual( | ||
ctxt: DeserializationContext, | ||
property: BeanProperty? | ||
): JsonDeserializer<CodifiedEnum<TEnum, TCode>> { | ||
return CodifiedEnumDeserializer(ctxt.contextualType) | ||
} | ||
|
||
@Suppress("UNCHECKED_CAST") | ||
constructor(contextualType: JavaType) : this() { | ||
this.enumType = contextualType.bindings.getBoundType(0).rawClass as Class<TEnum> | ||
this.enumCodeType = contextualType.bindings.getBoundType(1).rawClass as Class<TCode> | ||
} | ||
} |
12 changes: 12 additions & 0 deletions
12
enums-jackson/src/main/kotlin/pl/brightinventions/codified/jackson/CodifiedEnumSerializer.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
package pl.brightinventions.codified.jackson | ||
|
||
import com.fasterxml.jackson.core.JsonGenerator | ||
import com.fasterxml.jackson.databind.SerializerProvider | ||
import com.fasterxml.jackson.databind.ser.std.StdSerializer | ||
import pl.brightinventions.codified.enums.CodifiedEnum | ||
|
||
internal class CodifiedEnumSerializer : StdSerializer<CodifiedEnum<*, *>>(CodifiedEnum::class.java) { | ||
override fun serialize(value: CodifiedEnum<*, *>, gen: JsonGenerator, serializers: SerializerProvider) { | ||
serializers.defaultSerializeValue(value.code(), gen) | ||
} | ||
} |
36 changes: 36 additions & 0 deletions
36
enums-jackson/src/main/kotlin/pl/brightinventions/codified/jackson/CodifiedJacksonModule.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
package pl.brightinventions.codified.jackson | ||
|
||
import com.fasterxml.jackson.core.Version | ||
import com.fasterxml.jackson.databind.Module | ||
import com.fasterxml.jackson.databind.module.SimpleDeserializers | ||
import com.fasterxml.jackson.databind.module.SimpleSerializers | ||
import pl.brightinventions.codified.Codified | ||
import pl.brightinventions.codified.enums.CodifiedEnum | ||
|
||
class CodifiedJacksonModule : Module() { | ||
override fun version(): Version { | ||
return Version.unknownVersion() | ||
} | ||
|
||
override fun getModuleName(): String { | ||
return CodifiedJacksonModule::class.qualifiedName!! | ||
} | ||
|
||
override fun setupModule(context: SetupContext) { | ||
context.addSerializers( | ||
SimpleSerializers( | ||
listOf( | ||
CodifiedEnumSerializer(), | ||
CodifiedSerializer() | ||
) | ||
) | ||
) | ||
|
||
val simpleDeserializers = SimpleDeserializers().apply { | ||
addDeserializer(CodifiedEnum::class.java, CodifiedEnumDeserializer::class.java.newInstance()) | ||
addDeserializer(Codified::class.java, CodifiedDeserializer::class.java.newInstance()) | ||
} | ||
context.addDeserializers(simpleDeserializers) | ||
} | ||
} | ||
|
12 changes: 12 additions & 0 deletions
12
enums-jackson/src/main/kotlin/pl/brightinventions/codified/jackson/CodifiedSerializer.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
package pl.brightinventions.codified.jackson | ||
|
||
import com.fasterxml.jackson.core.JsonGenerator | ||
import com.fasterxml.jackson.databind.SerializerProvider | ||
import com.fasterxml.jackson.databind.ser.std.StdSerializer | ||
import pl.brightinventions.codified.Codified | ||
|
||
internal class CodifiedSerializer : StdSerializer<Codified<*>>(Codified::class.java) { | ||
override fun serialize(value: Codified<*>, gen: JsonGenerator, serializers: SerializerProvider) { | ||
serializers.defaultSerializeValue(value.code, gen) | ||
} | ||
} |
1 change: 1 addition & 0 deletions
1
enums-jackson/src/main/resources/META-INF/services/com.fasterxml.jackson.databind.Module
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
pl.brightinventions.codified.jackson.CodifiedJacksonModule |
212 changes: 212 additions & 0 deletions
212
...jackson/src/test/kotlin/pl/brightinventions/codified/jackson/CodifiedJacksonModuleTest.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,212 @@ | ||
package pl.brightinventions.codified.jackson | ||
|
||
import com.fasterxml.jackson.core.type.TypeReference | ||
import com.fasterxml.jackson.databind.ObjectMapper | ||
import com.fasterxml.jackson.databind.annotation.JsonDeserialize | ||
import org.junit.jupiter.api.Test | ||
import pl.brightinventions.codified.Codified | ||
import pl.brightinventions.codified.enums.CodifiedEnum | ||
import pl.miensol.shouldko.shouldEqual | ||
|
||
class CodifiedJacksonModuleTest { | ||
val objectMapper = ObjectMapper().findAndRegisterModules() | ||
|
||
@Test | ||
fun `can serialize and deserialize known value to codified enum with string`() { | ||
// given | ||
val input = Colour.Blue | ||
|
||
// when | ||
val output = serdeCodifiedEnum(input) | ||
|
||
// then | ||
output.knownOrNull().shouldEqual(input) | ||
} | ||
|
||
@Test | ||
fun `can serialize and deserialize known value to codified enum with int`() { | ||
// given | ||
val input = Weight.Heavy | ||
|
||
// when | ||
val output = serdeCodifiedEnum(input) | ||
|
||
// then | ||
output.knownOrNull().shouldEqual(input) | ||
} | ||
|
||
@Test | ||
fun `can serialize and deserialize not known value to codified enum`() { | ||
// given | ||
val input = "pink" | ||
|
||
// when | ||
val output = serdeCodifiedEnum(input) | ||
|
||
// then | ||
output.knownOrNull().shouldEqual(null) | ||
output.code().shouldEqual("pink") | ||
} | ||
|
||
@Test | ||
fun `can serialize and deserialize known value to enum property with string`() { | ||
// given | ||
val input = HasColour(Colour.Blue) | ||
|
||
// when | ||
val output = serde(input) | ||
|
||
// then | ||
output.shouldEqual(input) | ||
} | ||
|
||
@Test | ||
fun `can serialize and deserialize known value to enum property with int`() { | ||
// given | ||
val input = HasWeight().apply { weight = Weight.Medium } | ||
|
||
// when | ||
val output = serde(input) | ||
|
||
// then | ||
output.shouldEqual(input) | ||
} | ||
|
||
@Test | ||
fun `can deserialize not known value to enum property`() { | ||
// given | ||
val input = """{"colour": "pink"}""" | ||
// when | ||
val output = objectMapper.readValue(input, HasColour::class.java) | ||
// then | ||
output.colour.shouldEqual(null) | ||
} | ||
|
||
@Test | ||
fun `can deserialize known value to enum property`() { | ||
// given | ||
val input = """{"colour": "blue"}""" | ||
// when | ||
val output = objectMapper.readValue(input, HasColour::class.java) | ||
// then | ||
output.colour.shouldEqual(Colour.Blue) | ||
} | ||
|
||
@Test | ||
fun `can deserialize known value to codified enum property`() { | ||
// given | ||
val input = HasCodifiedColour(Colour.Blue) | ||
|
||
// when | ||
val output = serde(input) | ||
|
||
// then | ||
output.shouldEqual(input) | ||
} | ||
|
||
@Test | ||
fun `can deserialize not known value to codified enum property`() { | ||
// given | ||
val input = HasCodifiedColour("pink") | ||
// when | ||
val output = serde(input) | ||
// then | ||
output.colour!!.knownOrNull().shouldEqual(null) | ||
output.colour!!.code().shouldEqual("pink") | ||
} | ||
|
||
private fun serdeCodifiedEnum(input: Colour): CodifiedEnum<Colour, String> { | ||
val json = objectMapper.writer().writeValueAsString(input) | ||
return objectMapper.readValue(json, object : TypeReference<CodifiedEnum<Colour, String>>() {}) | ||
} | ||
|
||
private fun serdeCodifiedEnum(input: Weight): CodifiedEnum<Weight, Int> { | ||
val json = objectMapper.writer().writeValueAsString(input) | ||
return objectMapper.readValue(json, object : TypeReference<CodifiedEnum<Weight, Int>>() {}) | ||
} | ||
|
||
private fun serdeCodifiedEnum(input: String): CodifiedEnum<Colour, String> { | ||
val json = objectMapper.writer().writeValueAsString(input) | ||
return objectMapper.readValue(json, object : TypeReference<CodifiedEnum<Colour, String>>() {}) | ||
} | ||
|
||
private inline fun <reified T> serde(input: T): T { | ||
val json = objectMapper.writer().writeValueAsString(input) | ||
return objectMapper.readValue(json, T::class.java) | ||
} | ||
} | ||
|
||
|
||
@JsonDeserialize(using = CodifiedDeserializer::class) | ||
enum class Colour(override val code: String) : Codified<String> { | ||
Red("red"), Green("green"), Blue("blue"); | ||
} | ||
|
||
class HasColour() { | ||
var colour: Colour? = null | ||
|
||
constructor(colour: Colour) : this() { | ||
this.colour = colour | ||
} | ||
|
||
override fun equals(other: Any?): Boolean { | ||
if (this === other) return true | ||
if (javaClass != other?.javaClass) return false | ||
|
||
other as HasColour | ||
|
||
return colour == other.colour | ||
} | ||
|
||
override fun hashCode(): Int { | ||
return colour?.hashCode() ?: 0 | ||
} | ||
} | ||
|
||
class HasCodifiedColour() { | ||
var colour: CodifiedEnum<Colour, String>? = null | ||
|
||
constructor(colour: Colour) : this() { | ||
this.colour = CodifiedEnum.Known(colour) | ||
} | ||
|
||
constructor(colour: String) : this() { | ||
this.colour = CodifiedEnum.Unknown(colour) | ||
} | ||
|
||
override fun equals(other: Any?): Boolean { | ||
if (this === other) return true | ||
if (javaClass != other?.javaClass) return false | ||
|
||
other as HasCodifiedColour | ||
|
||
return colour == other.colour | ||
} | ||
|
||
override fun hashCode(): Int { | ||
return colour?.hashCode() ?: 0 | ||
} | ||
} | ||
|
||
|
||
@JsonDeserialize(using = CodifiedDeserializer::class) | ||
enum class Weight(override val code: Int) : Codified<Int> { | ||
Light(200), Medium(400), Heavy(60); | ||
} | ||
|
||
class HasWeight { | ||
var weight: Weight? = null | ||
override fun equals(other: Any?): Boolean { | ||
if (this === other) return true | ||
if (javaClass != other?.javaClass) return false | ||
|
||
other as HasWeight | ||
|
||
return weight == other.weight | ||
} | ||
|
||
override fun hashCode(): Int { | ||
return weight?.hashCode() ?: 0 | ||
} | ||
} | ||
|
Oops, something went wrong.