Moshi is a modern JSON library from Square, created specifically for Kotlin and Android with Gson’s limitations in mind. It is fully compatible with Kotlin null safety, generates code at compile time, and does not use reflection, which improves performance and reliability. According to Square Moshi, 2024, Moshi provides predictable serialization and supports custom adapters for any data types.
Key Takeaways
Moshi is a JSON library for JVM, Android, and Kotlin Multiplatform, created by Square (the authors of OkHttp and Retrofit). Unlike Gson, Moshi does not rely on reflection — adapters are generated at compile time via the @JsonClass(generateAdapter = true) annotation. This makes Moshi faster, safer, and more predictable when working with Kotlin-specific constructs.
The main difference between Moshi and its predecessors is the rejection of reflection. Reflection allows Gson to work with any class without preparation, but the cost is slow initialization, inability for compiler optimization, and the risk of runtime errors. Moshi requires explicit class declaration for code generation, but in return provides hand-written code speed and full type safety at compile time.
// Add Moshi to build.gradle
dependencies {
implementation "com.squareup.moshi:moshi:1.15.0"
implementation "com.squareup.moshi:moshi-kotlin:1.15.0"
kapt "com.squareup.moshi:moshi-kotlin-codegen:1.15.0"
}
// Simple model with code generation
@JsonClass(generateAdapter = true)
data class User(
@Json(name = "user_id")
val id: Int,
val name: String,
val email: String,
val avatar: String? = null
)
// Usage
val moshi = Moshi.Builder()
.build()
val jsonAdapter = moshi.adapter(User::class.java)
To start working with Moshi, you need to add dependencies to build.gradle and annotate the models. Moshi.Builder serves as the entry point: through it, built-in adapters for standard types, custom adapters are added, and the library's behavior is configured. Moshi supports adapters for Date, Enum, Collection, and Map out of the box, but Kotlin classes require the moshi-kotlin module. Unlike Gson, Moshi does not use reflection for Kotlin classes by default — for this, KotlinJsonAdapterFactory is connected, which serves as a fallback when code generation is not used or the class is not annotated with @JsonClass. This approach ensures that the developer explicitly chooses between code generation performance and reflection flexibility for each specific class.
After building Moshi through Builder, the developer gets a Moshi instance and requests an adapter for the desired class. JsonAdapter is the central object that performs serialization via toJson() and deserialization via fromJson(). Moshi automatically uses the generated adapter if the class is annotated with @JsonClass(generateAdapter = true), otherwise it applies the reflective KotlinJsonAdapterFactory as a fallback. This approach combines the speed of code generation with the flexibility of a reflective mechanism for projects of any scale and complexity. Moshi is well suited for both small applications and large enterprise projects with hundreds of data models.
// Configure Moshi with KotlinJsonAdapterFactory
val moshi = Moshi.Builder()
.add(KotlinJsonAdapterFactory())
.add(LocalDateAdapter())
.build()
// Using the adapter
val adapter = moshi.adapter(User::class.java)
// Serialization
val user = User(1, "Alice", "alice@test.com")
val json = adapter.toJson(user)
// Deserialization
val jsonString = """{"user_id":2,"name":"Bob","email":"bob@test.com"}"""
val parsedUser = adapter.fromJson(jsonString)
// Working with lists
val listAdapter = moshi.adapter(
Types.newParameterizedType(
List::class.java,
User::class.java
)
)
Moshi uses annotations to configure serialization and support custom types. @Json(name = "...") sets the JSON key for a field. @Transient excludes a field from serialization. @JsonClass(generateAdapter = true) enables code generation. For custom logic, Moshi provides @ToJson and @FromJson annotations, which can be placed in a separate adapter class.
The @Json annotation replaces Gson’s @SerializedName and works similarly: the kotlinName field is mapped to the JSON key "kotlin_name". For types that Moshi cannot serialize by default (e.g., LocalDate), the developer creates a class with @ToJson and @FromJson methods. Adapters are registered via Moshi.Builder.add() and apply globally or to a specific type. Moshi supports sealed classes and polymorphic serialization via @JsonClass with an explicit discriminator, allowing type hierarchies in JSON without manual field checking. During deserialization, Moshi ignores unknown JSON keys by default, ensuring backward compatibility when adding new fields on the server side without changing client code. For debugging, strict mode can be enabled via failOnUnknown, which throws an exception when unknown keys are encountered.
// Custom adapter for LocalDate
class LocalDateAdapter {
@ToJson
fun toJson(date: LocalDate): String {
return date.format(DateTimeFormatter.ISO_LOCAL_DATE)
}
@FromJson
fun fromJson(dateString: String): LocalDate {
return LocalDate.parse(dateString)
}
}
// Model with Moshi annotations
@JsonClass(generateAdapter = true)
data class Event(
@Json(name = "event_id")
val id: Int,
@Json(name = "event_date")
val date: LocalDate,
@Transient
val localCache: String? = null
)
// Register the adapter
val moshi = Moshi.Builder()
.add(LocalDateAdapter())
.add(KotlinJsonAdapterFactory())
.build()
Comparing Moshi and Gson is a common question when choosing a JSON library for an Android project. Moshi wins in modern Kotlin development due to code generation, null safety, and speed. Gson remains relevant for Java projects, legacy code, and scenarios where minimal configuration is important. The difference becomes noticeable with large data volumes and complex models.
Performance tests show that Moshi with code generation is 2–5 times faster than Gson on serialization and deserialization operations. The key advantage of Moshi is correct handling of Kotlin null safety: if a field is missing in JSON and the model declares it as non-null without a default value, Moshi throws an exception at deserialization time, preventing hidden errors.
| Characteristic | Gson | Moshi |
|---|---|---|
| Mechanism | reflection | code generation / reflection |
| Null safety | not supported | full Kotlin support |
| Speed | average | high |
| Default values | not supported | supported |
| Kotlin Multiplatform | no | yes |
| Library size | ~240 Kb | ~150 Kb |
The choice between Moshi and Gson depends on the project context. New Kotlin projects benefit from Moshi thanks to type safety and performance. Gson remains a reasonable choice for supporting Java code, dynamic JSON structures, or when simplicity of setup matters more than speed. For Kotlin Multiplatform, Moshi is the only one of the two options that supports this platform.
When migrating from Gson to Moshi, the main changes concern annotations and adapters. Gson’s @SerializedName is replaced with @Json(name = "..."), and custom JsonSerializer/JsonDeserializer with the @ToJson/@FromJson pair. For models with default values and nullable fields, Moshi behaves more predictably: if a non-null field without a default is missing in JSON, Moshi throws JsonDataException, preventing hidden NPEs. Integration with Retrofit via MoshiConverterFactory is added with a single dependency and does not require changing the network layer architecture. For obfuscation via ProGuard or R8, you need to add rules to preserve @JsonClass-annotated classes and generated adapters, otherwise serialization will break in the release build. Overall, migrating from Gson to Moshi is justified in new Kotlin projects where performance and type safety are important.
// Serialization comparison: Gson vs Moshi
data class Sample(
val name: String,
val count: Int,
val tags: List<String> = listOf()
)
// Gson: works through reflection
val gson = Gson()
val fromGson = gson.fromJson("""{"name":"test"}""",
Sample::class.java)
// count = 0 (default), but null safety is not checked
// Moshi: requires an adapter, null safety is explicit
@JsonClass(generateAdapter = true)
data class SampleMoshi(
val name: String,
val count: Int,
val tags: List<String> = listOf()
)
Frequently Asked Questions
Moshi is a JSON library from Square for Kotlin and Android that uses code generation instead of reflection. It provides high performance, correct handling of Kotlin null safety, and compatibility with Kotlin Multiplatform.
Moshi surpasses Gson in speed (2–5 times faster thanks to code generation), safety (respects Kotlin null annotations), and size (~90 Kb smaller). Moshi also supports Kotlin Multiplatform and default values in data classes.
@JsonClass(generateAdapter = true) instructs Moshi to generate an adapter for the given class at compile time. The generated adapter performs serialization directly, without reflection, providing maximum performance.
Create a class with methods annotated with @ToJson (serialization) and @FromJson (deserialization). Register the instance via Moshi.Builder.add(). Moshi will automatically find and apply the adapter when working with the corresponding type.
Yes, Moshi supports Kotlin Multiplatform starting from version 1.13.0. This makes it the only popular JSON solution for KMP projects, allowing you to use common serialization code across all target platforms.
Summary
We will develop a mobile application turnkey
IT Sectr creates iOS and Android applications for startups and businesses since 2017. We will advise you and propose the best solution.
Read also