PCSalt
YouTube GitHub
Back to Kotlin
Kotlin (Updated: ) · 3 min read

JSON Parsing in Kotlin — Which Library Should You Use?

A side-by-side comparison of org.json, Gson, Moshi, Kotlin Serialization, and Jackson for JSON parsing in Kotlin.


This is Part 6 — the final post in the JSON Parsing in Kotlin series. We’ve parsed the same JSON response with five different libraries. Now let’s put them all side by side and figure out which one you should actually use.

The JSON (One Last Time)

Throughout this series, we parsed this JSON — a typical SaaS API response with users, subscriptions, teams, and pagination:

{
  "status": "success",
  "data": {
    "users": [
      {
        "id": "usr_a1b2c3d4",
        "name": "Navkrishna",
        "email": "[email protected]",
        "role": "ADMIN",
        "verified": true,
        "createdAt": "2026-01-15T10:30:00Z",
        "profile": {
          "avatar": "https://cdn.example.com/avatars/nav.jpg",
          "bio": "Full-stack developer",
          "social": {
            "github": "krrishnaaaa",
            "twitter": null,
            "linkedin": "navkrishna"
          }
        },
        "subscription": {
          "plan": "PRO",
          "billingCycle": "YEARLY",
          "price": 199.99,
          "features": ["analytics", "api_access", "priority_support"],
          "trialEndsAt": null
        },
        "teams": [
          {
            "id": "team_x1y2",
            "name": "Backend",
            "role": "OWNER",
            "memberCount": 5
          },
          {
            "id": "team_z3w4",
            "name": "Mobile",
            "role": "MEMBER",
            "memberCount": 3
          }
        ],
        "lastLoginAt": "2026-03-14T18:45:00Z"
      },
      {
        "id": "usr_e5f6g7h8",
        "name": "Priya Sharma",
        "email": "[email protected]",
        "role": "MEMBER",
        "verified": false,
        "createdAt": "2026-03-01T14:00:00Z",
        "profile": {
          "avatar": null,
          "bio": null,
          "social": {
            "github": "priya-dev",
            "twitter": "priyacodes",
            "linkedin": null
          }
        },
        "subscription": {
          "plan": "TRIAL",
          "billingCycle": null,
          "price": 0.0,
          "features": ["analytics"],
          "trialEndsAt": "2026-03-31T23:59:59Z"
        },
        "teams": [],
        "lastLoginAt": null
      }
    ],
    "pagination": {
      "page": 1,
      "perPage": 20,
      "totalItems": 2,
      "totalPages": 1,
      "hasNext": false
    }
  },
  "meta": {
    "requestId": "req_9k8j7h6g",
    "timestamp": "2026-03-15T00:00:00Z",
    "apiVersion": "v2"
  }
}

The Comparison Table

Featureorg.jsonGsonMoshiKotlin SerializationJackson
Lines of parsing code~100~5~5~3~5
Null safetyManual (isNull())Silent nulls in non-null fieldsFails on null in non-nullEnforced by compilerKotlin module aware
Kotlin-nativeNoNoPartial (codegen)YesNo (module bridge)
ReflectionN/A (manual)YesOptional (codegen)No (compiler plugin)Yes
Code generationNoneNoneAnnotation processorCompiler pluginNone
Date handlingManualCustom adapterCustom adapterCustom serializerJavaTimeModule
Android supportBuilt-inGoodExcellentExcellentHeavy
Spring Boot supportNoneManual setupManual setupGrowingDefault (built-in)
Bundle size~80 KB~280 KB~150 KB~500 KB~1.5 MB
Kotlin MultiplatformNoNoNoYesNo
Proguard rules neededNoYesYes (reflection) / No (codegen)NoYes

Code Comparison

Here’s the parse call for the same JSON with all five libraries:

org.json — Manual Parsing

import org.json.JSONObject

fun parse(jsonString: String): ApiResponse {
  val root = JSONObject(jsonString)
  val status = root.getString("status")
  val data = root.getJSONObject("data")
  val users = parseUsers(data.getJSONArray("users"))
  val pagination = parsePagination(data.getJSONObject("pagination"))
  val meta = parseMeta(root.getJSONObject("meta"))
  return ApiResponse(status, Data(users, pagination), meta)
}
// + ~90 more lines of parseUser(), parseProfile(), parseTeams()...

Gson

import com.google.gson.Gson
import com.google.gson.GsonBuilder
import java.time.Instant

val gson: Gson = GsonBuilder()
  .registerTypeAdapter(Instant::class.java, InstantDeserializer())
  .create()

fun parse(jsonString: String): ApiResponse {
  return gson.fromJson(jsonString, ApiResponse::class.java)
}

Moshi (with codegen)

import com.squareup.moshi.Moshi
import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory

val moshi: Moshi = Moshi.Builder()
  .add(InstantAdapter())
  .addLast(KotlinJsonAdapterFactory())
  .build()

fun parse(jsonString: String): ApiResponse {
  val adapter = moshi.adapter(ApiResponse::class.java)
  return adapter.fromJson(jsonString)!!
}

Kotlin Serialization

import kotlinx.serialization.json.Json

val json = Json { ignoreUnknownKeys = true }

fun parse(jsonString: String): ApiResponse {
  return json.decodeFromString<ApiResponse>(jsonString)
}

Jackson

import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule
import com.fasterxml.jackson.module.kotlin.readValue
import com.fasterxml.jackson.module.kotlin.registerKotlinModule

val mapper: ObjectMapper = ObjectMapper()
  .registerKotlinModule()
  .registerModule(JavaTimeModule())

fun parse(jsonString: String): ApiResponse {
  return mapper.readValue<ApiResponse>(jsonString)
}

The difference is stark. org.json needs ~100 lines. Every other library needs ~5 or fewer. The real differences between Gson, Moshi, Kotlin Serialization, and Jackson are in the setup and safety guarantees, not the parse call itself.

When to Use What

Android app — Kotlin Serialization or Moshi

Both are lightweight, fast, and designed for mobile. Kotlin Serialization is the newer, Kotlin-native choice with no reflection. Moshi with codegen is the battle-tested alternative. Either way, you get small APK size and no Proguard headaches.

If you’re starting a new Android project today, go with Kotlin Serialization. Google recommends it, and it’s the direction the ecosystem is moving.

Spring Boot — Jackson (default) or Kotlin Serialization

Jackson is already included in Spring Boot. Zero configuration, zero additional dependencies. If you’re in the Spring ecosystem, Jackson is the path of least resistance.

Kotlin Serialization support in Spring is improving and can be configured as an alternative, but Jackson remains the pragmatic choice for most Spring Boot projects.

Kotlin Multiplatform — Kotlin Serialization (only option)

If you’re sharing code across JVM, JS, Native, or Wasm, Kotlin Serialization is your only choice. The other libraries are JVM-only. This alone makes it the future-proof pick.

Legacy codebase — Gson

If you’re maintaining an existing codebase that already uses Gson, there’s no urgent reason to migrate. Gson works fine with Kotlin data classes (with some caveats around null safety). Just be aware that it’s in maintenance mode — don’t expect new features.

Quick script or prototype — org.json

If you’re writing a quick script to extract one field from a JSON file, org.json is fine. No model classes, no setup — just JSONObject(string).getString("key"). But the moment your JSON gets complex, switch to a real library.

The Verdict

For most Kotlin projects in 2026, the answer is:

  1. Kotlin Serialization — if you want the Kotlin-native, compile-time safe, multiplatform-ready approach
  2. Jackson — if you’re in Spring Boot and want zero-config JSON that just works
  3. Moshi — if you want a mature, Android-optimized library with a great Kotlin story

Gson and org.json have their place, but they’re not where you want to start a new project.

Series Recap

PartLibraryPost
1org.jsonManual Parsing with org.json
2GsonParsing with Gson
3MoshiParsing with Moshi
4Kotlin SerializationParsing with Kotlin Serialization
5JacksonParsing with Jackson
6ComparisonThis post

Looking for Java? The same comparison without Kotlin Serialization is here.