General
JSON to Kotlin data class: Gson vs Moshi vs kotlinx.serialization annotations
0 views

Turning a JSON payload into a Kotlin data class looks trivial until you hit the details: which annotations, what about snake_case, what happens to missing fields? The answers differ meaningfully across the three mainstream libraries. Here's the same payload, three ways.
The JSON:
{
"user_id": "u-1042",
"display_name": "Yush",
"is_verified": true,
"follower_count": 1204,
"last_login": null
}
Gson
data class UserProfile(
@SerializedName("user_id") val userId: String,
@SerializedName("display_name") val displayName: String,
@SerializedName("is_verified") val isVerified: Boolean,
@SerializedName("follower_count") val followerCount: Int,
@SerializedName("last_login") val lastLogin: String?
)
The catch everyone learns the hard way: Gson ignores Kotlin null-safety. It uses reflection and will happily write null into a non-nullable val if the field is missing — you get the crash later, far from the parse site. Default values are also ignored unless every field has one. Gson is fine for legacy codebases, but it predates Kotlin and it shows.
Moshi (with KSP codegen)
@JsonClass(generateAdapter = true)
data class UserProfile(
@Json(name = "user_id") val userId: String,
@Json(name = "display_name") val displayName: String,
@Json(name = "is_verified") val isVerified: Boolean,
@Json(name = "follower_count") val followerCount: Int,
@Json(name = "last_login") val lastLogin: String?
)
With codegen (generateAdapter = true), Moshi enforces nullability at parse time: a missing non-nullable field throws a clear JsonDataException naming the field. Default values work as expected. This is the pragmatic choice for Android projects already on OkHttp/Retrofit.
kotlinx.serialization
@Serializable
data class UserProfile(
@SerialName("user_id") val userId: String,
@SerialName("display_name") val displayName: String,
@SerialName("is_verified") val isVerified: Boolean,
@SerialName("follower_count") val followerCount: Int,
@SerialName("last_login") val lastLogin: String? = null
)
Compiler-plugin based, zero reflection, multiplatform (JVM/JS/Native/Wasm). Two behaviors worth knowing: unknown JSON keys fail by default (turn on ignoreUnknownKeys = true in the Json {} config for API work), and fields with default values are skipped during serialization unless you set encodeDefaults = true. If you're starting fresh or targeting KMP, this is the one.
Quick comparison
Gson
Moshi (codegen)
kotlinx.serialization
Null-safety enforced
❌ silently breaks
✅ throws clearly
✅ throws clearly
Default values
unreliable
✅
✅
Reflection at runtime
yes
no (codegen)
no (compiler plugin)
Multiplatform
❌
❌
✅
Best for
legacy code
Android + Retrofit
new code, KMP
Generating the boilerplate
Writing @SerialName for thirty fields by hand is nobody's idea of fun. I use this JSON to Kotlin converter — paste the payload, and it derives the data class with nested classes and nullable types from the actual values, in the browser (nothing uploaded). Then switch the annotations to whichever library your project uses.
TL;DR
Gson silently violates Kotlin null-safety; prefer Moshi or kotlinx.serialization.
Moshi: best drop-in for Android/Retrofit stacks.
kotlinx.serialization: default for new projects and multiplatform — remember ignoreUnknownKeys.
Read original: https://dev.to/yuus_company/json-to-kotlin-data-class-gson-vs-moshi-vs-kotlinxserialization-annotations-34go
PreviousJPA/Hibernate Entity Lifecycle: 4 States Explained Simply! 💡NextJSON to String Conversion: The Edge Cases That Actually Bite in Production
Related posts
Vellinko – Sign PDFs in the browser without uploading them
0Hacker News New
Show HN: Vivio House, an interactive 3D headquarters
0Hacker News New
Pushover – Simple Notifications
0Hacker News New
The Gift of It's Your Problem Now (2021)
0Hacker News New
Implied News: News X Prediction Markets
0Hacker News New
Show HN: OpenDocBot - bring your own model to Word, Excel and PowerPoint
0Hacker News New