Safely Navigating the Transition: From Gson to kotlinx.serialization
Introduction

Embarking on the journey from Gson
to kotlinx.serialization
is like upgrading your app to a more Kotlin-friendly and optimized serialization experience. Let’s dive into why we chose kotlinx.serialization
, the challenges we faced during the transition, and the solutions that made the migration smooth and reliable. 🚀✨
Embracing kotlinx.serialization
In the world of Android development, where Kotlin rules, kotlinx.serialization
is like the perfect sidekick. This library not only blends seamlessly but also follows a Kotlin-first approach, aligning beautifully with the language’s philosophy.🌐
Kotlin-Centric Approach
Say goodbye to the old ways of Gson
; kotlinx.serialization
puts Kotlin front and center. It taps into Kotlin’s native features like data classes and sealed classes, making data serialization and deserialization a breeze.🔄
Compile-Time Safety
The real charm here is the heightened compile-time safety. It’s like having a superhero cape that reduces runtime errors related to serialization and deserialization, giving me more confidence in my code.🦸♂️
Performance Gains
And the perks don’t stop there! kotlinx.serialization
brings some serious performance gains. Optimized for Kotlin, it promises efficient serialization and deserialization, giving a nice boost to the overall application performance.⚡️
Problems and Challenges
Default Value Issues
Transitioning from Gson
to kotlinx.serialization
brought its set of challenges around default values. The different approaches required careful handling to ensure consistent serialization and deserialization.🚧
//kotlinx.serialization
@Serializable
data class SomeDto(
// Default value should be provided
@SerialName("some_optional_key")
val optionalKey: Int = 0,
)
// Gson
data class SomeDto(
@SerializedName("some_optional_key")
val optionalKey: Int,
)
Nullable Values and Platform Type Issues
Dealing with nullable values and platform types needed full attention. The migration called for a thoughtful strategy to prevent any unexpected runtime issues.💡
//kotlinx.serialization
@Serializable
data class SomeDto(
// Exception would be thrown if the key is not present in the json
// even if optionalKey is not accessed in the code
@SerialName("some_key")
val key: String,
)
// Gson
data class SomeDto(
// No exception would be thrown if optionalKey is not accessed in the code
@SerializedName("some_key")
val key: String,
)
Lack of Documentation and Testing
Navigating through insufficient documentation and testing gaps for API response deserialization added a bit of spice to the migration process.📚🔍
Solutions Employed
Leveraging IDE Tools
To make life easier, I dived into the arsenal of IDE tools in Android Studio. Using regex and Find/Replace, I seamlessly integrated kotlinx.serialization
annotations alongside Gson
annotations.🧰

// Before
@SerializedName("field_name")
val fieldName: String
// After
@SerializedName("field_name")
@SerialName("field_name")
val fieldName: String
Feature Flags for Safe Merging
Ensuring a smooth merging of changes into the main branch, I used feature flags. Check out more about feature flags here. This allowed for a gradual introduction of kotlinx.serialization
while keeping Gson
as the primary serializer until I was sure the transition was a success.🚩
if (useKtxSerialization) {
// Use kotlinx.serialization
} else {
// Use Gson
}
Combined Converter for Retrofit
Implementing a combined converter for Retrofit was a game-changer. This converter supported both Gson
and kotlinx.serialization
, making the incremental migration of endpoints a breeze while keeping our app stable.🚗💨
// Retrofit setup with combined converter
val retrofit = Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(
CombinedConverterFactory(
KotlinxSerializationConverterFactory.create(),
GsonConverterFactory.create()
)
)
.build()
Polymorphic Serialization
Polymorphic serialization in kotlinx.serialization
is a breeze. Let’s say we have a hierarchy of shapes:
@Serializable
@Polymorphic
sealed class Shape
@Serializable
@SerialName("CircleType")
data class Circle(val radius: Double) : Shape()
@Serializable
@SerialName("SquareType")
data class Square(val sideLength: Double) : Shape()
Now, when serializing a list of shapes:
val shapes: List<Shape> = listOf(Circle(5.0), Square(4.0))
val json = Json.encodeToString(shapes)
// JSON
[
{"type":"CircleType","radius":5.0},
{"type":"SquareType","sideLength":4.0}
]
Writing Tests for Complex Objects
To tackle challenges with custom serializers and polymorphics, I went all out with comprehensive tests for complex serializable objects. This proactive approach, helped me catch issues early in the migration process.🧪
private val jsonString = """
{
"some_object": {
"type": "SOME_TYPE",
"payload" : {
"some_key": "some_value"
}
}
}""".trimIndent()
// Example test for complex serializable object
@Test
fun testComplexObjectSerialization() {
// Test logic using kotlinx.serialization
// Assert statements
val someObject = json.decodeFromString<SomeClass>(jsonString)
assertEquals(
expectedModel, someObject
)
}
Fallback to Gson
for Stability
To keep our app sailing smoothly, I introduced Gson
as a fallback for kotlinx.serialization
. This dual support allowed me to confidently navigate unexpected challenges during the migration.⚓️
// Fallback logic in case kotlinx.serialization fails
try {
// Use kotlinx.serialization
} catch (e: SerializationException) {
// Monitor exception
// Fallback to Gson
}
Incremental Stability Monitoring
To ensure stability incrementally, I enlisted monitoring tools to track issues arising from kotlinx.serialization
usage. This proactive approach helped us identify and resolve challenges as they surfaced.🔍📈
Conclusion
In conclusion, the transition from Gson
to kotlinx.serialization
wasn’t without its quirks, but with some informed decisions and a strategic approach, We successfully navigated the migration. Leveraging IDE tools, using feature flags, writing comprehensive tests, and keeping Gson
as a fallback ensured a safe and incremental transition. The lessons learned and strategies I employed serve as a friendly guide for others embarking on a similar journey. May your migration endeavors be as smooth and rewarding! 🚀✨