skip to content

How do you configure Moshi so that its adapters are generated at compile time, and how does that wiring differ from Moshi's reflection-based approach?

level: middleimportance: should knowfreq 45%

answer

  1. Codegen: ksp(moshi-kotlin-codegen) + @JsonClass(generateAdapter=true)
  2. Reflection: moshi-kotlin + KotlinJsonAdapterFactory(), no processor
  3. moshi-kotlin pulls in kotlin-reflect
  4. Codegen = faster, R8-friendly, compile-time errors
  5. Moshi codegen is KSP-native now

basics

~10 s

For compile-time adapters you add Moshi plus its codegen artifact on the ksp(...) configuration and mark classes @JsonClass(generateAdapter = true). The reflection approach instead adds moshi-kotlin and needs no processor at all.

solid answer

~30 s

Moshi has two paths. Codegen: add implementation("com.squareup.moshi:moshi") and ksp("com.squareup.moshi:moshi-kotlin-codegen"), apply the KSP plugin, and annotate data classes with @JsonClass(generateAdapter = true); Moshi's KSP processor generates a JsonAdapter per class at build time. Moshi ships native KSP support (it was kapt before). Reflection: add implementation("com.squareup.moshi:moshi-kotlin") and register KotlinJsonAdapterFactory on the Moshi.Builder — no processor, no KSP plugin, but it pulls in kotlin-reflect and resolves adapters at runtime, which is slower and larger. Codegen is preferred for production: no reflection, faster startup, R8-friendly, and errors surface at compile time.

code

kotlin · 9 lines
kotlin
// Codegen path
plugins { id("com.google.devtools.ksp") }
dependencies {
    implementation("com.squareup.moshi:moshi")
    ksp("com.squareup.moshi:moshi-kotlin-codegen")
}

@JsonClass(generateAdapter = true)
data class User(val id: Long, val name: String)

go deeper

for a junior

Knows codegen needs the ksp(...) artifact plus the @JsonClass annotation.

for a middle

Contrasts codegen vs reflection wiring and the kotlin-reflect cost.

for a senior

Explains coexistence (factory added last), R8 implications, and compile-time error surfacing.

for a principal

Weighs binary-size/startup tradeoffs at scale and standardizes the codegen choice across modules.

## Moshi's two ways to make adapters Moshi needs a `JsonAdapter<T>` to (de)serialize each type. There are two strategies: ### 1. Codegen (compile-time, preferred) A KSP processor reads `@JsonClass(generateAdapter = true)` and emits a generated `XxxJsonAdapter` at build time. ```kotlin plugins { id("com.google.devtools.ksp") } dependencies { implementation("com.squareup.moshi:moshi") ksp("com.squareup.moshi:moshi-kotlin-codegen") } ``` ```kotlin @JsonClass(generateAdapter = true) data class User(val id: Long, val name: String) ``` - **No reflection** at runtime → faster startup, smaller, R8/ProGuard-friendly. - Errors (e.g., unsupported type) appear **at compile time**. - `moshi-kotlin-codegen` ships **native KSP** support (historically kapt). ### 2. Reflection (runtime) Add `moshi-kotlin` and register the factory: ```kotlin dependencies { implementation("com.squareup.moshi:moshi-kotlin") } ``` ```kotlin val moshi = Moshi.Builder() .add(KotlinJsonAdapterFactory()) .build() ``` - **No processor, no KSP plugin** needed. - Pulls in **kotlin-reflect** (large) and resolves adapters via reflection at runtime → slower, bigger, needs keep rules. ## Which to choose Production code should prefer **codegen** for performance and build-time safety. Reflection is fine for quick prototypes or when you can't run a processor. The two can coexist: codegen handles annotated classes, and the reflective factory (added **last** in the builder chain) covers the rest. ## Key wiring takeaway The distinguishing config is `ksp("moshi-kotlin-codegen")` + `@JsonClass(generateAdapter = true)` for codegen versus a plain `moshi-kotlin` dependency + `KotlinJsonAdapterFactory()` for reflection.

  • If you use codegen, do you still need KotlinJsonAdapterFactory?
    No — codegen-generated adapters are registered automatically via @JsonClass. You'd only add the reflective factory to also handle classes you didn't annotate, and it should be added last.
  • Why is the reflection approach discouraged for production?
    It depends on kotlin-reflect (large dependency), resolves adapters at runtime (slower startup), and needs extra R8/ProGuard keep rules to survive minification.

saying these in an interview costs you the question

  • Using moshi-kotlin-codegen on implementation instead of ksp
  • Forgetting @JsonClass(generateAdapter = true) and wondering why no adapter exists
  • Thinking reflection requires the KSP plugin
  • Claiming Moshi codegen still requires kapt

context