Last active
November 15, 2022 02:08
-
-
Save vshivam/5fd90e87be8958f5c9e1d1ff0c55f798 to your computer and use it in GitHub Desktop.
When a mobile client interacts with backend, api contracts are subject to change heavily during the development phase. With this custom serializer we can ensure that it is "okay" to break contracts. Properties can be added, removed and updated without the fear of "older clients" breaking.
This file contains hidden or 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
class AlphaPropertySerializer<T>( | |
private val valueSerializer: KSerializer<T> | |
) : KSerializer<AlphaProperty<T>> { | |
override val descriptor: SerialDescriptor = valueSerializer.descriptor | |
override fun deserialize(decoder: Decoder): AlphaProperty<T> { | |
decoder as JsonDecoder | |
val jsonElement: JsonElement = decoder.decodeJsonElement() | |
return try { | |
AlphaProperty.Known(decoder.json.decodeFromJsonElement(valueSerializer, jsonElement)) | |
} catch (e: Exception) { | |
AlphaProperty.Unknown | |
} | |
} | |
override fun serialize(encoder: Encoder, value: AlphaProperty<T>) { | |
when (value) { | |
AlphaProperty.Unknown -> throw SerializationException( | |
"Tried to serialize an optional property that had no value present." | |
) | |
is AlphaProperty.Known -> valueSerializer.serialize(encoder, value.value) | |
} | |
} | |
} | |
sealed class AlphaProperty<out T> { | |
object Unknown : AlphaProperty<Nothing>() | |
data class Known<T>(val value: T) : AlphaProperty<T>() | |
} |
This file contains hidden or 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
class AlphaPropertySerializerTest { | |
@Test | |
fun verifyParsingWhenJsonIsCorrect() { | |
val actual = json.decodeFromString( | |
deserializer = SampleWidget.serializer(), | |
string = """{"customerId" : "customer_id","tip" : {"amount": 3, "note" : "Thank you!"}}""" | |
) | |
assertThat(actual).isEqualTo( | |
SampleWidget( | |
tip = AlphaProperty.Known(Tip(3, "Thank you!")), | |
customerId = AlphaProperty.Known("customer_id") | |
) | |
) | |
} | |
@Test | |
fun verifyParsingWhenTipChangesToPrimitive() { | |
val actual = json.decodeFromString( | |
deserializer = SampleWidget.serializer(), | |
string = """{"tip" : 3}""" | |
) | |
assertThat(actual).isEqualTo( | |
SampleWidget( | |
customerId = null, | |
tip = AlphaProperty.Unknown | |
) | |
) | |
} | |
@Test | |
fun verifyParsingWhenCustomerIdChangesToObject() { | |
val actual = json.decodeFromString( | |
deserializer = SampleWidget.serializer(), | |
string = """{"customerId": { "uuid" : "uuid"}, "tip" : {"amount": 10, "note" : "Thank you!"}}""" | |
) | |
assertThat(actual).isEqualTo( | |
SampleWidget( | |
customerId = AlphaProperty.Unknown, | |
tip = AlphaProperty.Known(Tip(10, "Thank you!")) | |
) | |
) | |
} | |
} | |
private val json = Json( | |
builderAction = { | |
coerceInputValues = true | |
ignoreUnknownKeys = true | |
explicitNulls = false | |
this.isLenient = isLenient | |
this.encodeDefaults = false | |
} | |
) |
This file contains hidden or 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
@Serializable | |
data class Tip(val amount: Int, val note: String) | |
@Serializable | |
data class SampleWidget( | |
@Serializable(with = AlphaPropertySerializer::class) | |
val customerId: AlphaProperty<String>?, | |
@Serializable(with = AlphaPropertySerializer::class) | |
val tip: AlphaProperty<Tip>?, | |
) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment