Try it yourself with our free Json Formatter tool — runs entirely in your browser, no signup needed.

How to Flatten nested JSON in Kotlin

How to flatten nested JSON in Kotlin

Flattening nested JSON is a common task in data processing, especially when working with APIs that return complex, hierarchical data. In this article, we'll explore how to flatten nested JSON in Kotlin, a modern, statically typed language that's gaining popularity. By the end of this guide, you'll be able to take a nested JSON object and transform it into a flat, easy-to-work-with data structure.

Quick Example

Here's a minimal example that flattens a nested JSON object using the popular kotlinx.serialization library:

import kotlinx.serialization.decodeFromString
import kotlinx.serialization.json.Json

data class NestedJson(val name: String, val address: Address)
data class Address(val street: String, val city: String)

fun main() {
    val json = """
        {
            "name": "John Doe",
            "address": {
                "street": "123 Main St",
                "city": "Anytown"
            }
        }
    """.trimIndent()

    val jsonDecoder = Json { ignoreUnknownKeys = true }
    val nestedJson = jsonDecoder.decodeFromString<NestedJson>(json)

    val flatJson = nestedJson.run {
        mapOf(
            "name" to name,
            "street" to address.street,
            "city" to address.city
        )
    }

    println(flatJson) // {name=John Doe, street=123 Main St, city=Anytown}
}

To use this code, add the following dependency to your build.gradle file:

dependencies {
    implementation 'org.jetbrains.kotlinx:kotlinx-serialization-json:1.3.2'
}

Step-by-Step Breakdown

Let's walk through the code:

  1. We define two data classes, NestedJson and Address, to represent the nested JSON structure.
  2. In the main function, we define a JSON string with a nested object.
  3. We create a Json decoder with the ignoreUnknownKeys option set to true, which allows us to decode JSON with unexpected properties.
  4. We decode the JSON string into a NestedJson object using the decodeFromString function.
  5. We use the run function to create a new mapOf with the flattened key-value pairs.

Handling Edge Cases

Here are some common edge cases to consider:

Empty/null input

If the input JSON is empty or null, the decoder will throw a SerializationException. To handle this, you can add a null check before decoding:

if (json != null) {
    val nestedJson = jsonDecoder.decodeFromString<NestedJson>(json)
    // ...
} else {
    println("Invalid input")
}

Invalid input

If the input JSON is invalid (e.g., malformed or missing required properties), the decoder will throw a SerializationException. To handle this, you can catch the exception and provide a meaningful error message:

try {
    val nestedJson = jsonDecoder.decodeFromString<NestedJson>(json)
    // ...
} catch (e: SerializationException) {
    println("Invalid input: ${e.message}")
}

Large input

For large JSON inputs, you may want to consider using a streaming JSON parser to avoid loading the entire JSON into memory. One option is to use the kotlinx.serialization library's JsonReader class:

val jsonReader = JsonReader(json)
val nestedJson = jsonReader.decode<NestedJson>()

Unicode/special characters

If your JSON contains Unicode or special characters, make sure to use a JSON decoder that supports these characters. The kotlinx.serialization library's Json decoder supports Unicode characters by default.

Common Mistakes

Here are some common mistakes developers make when flattening nested JSON in Kotlin:

Mistake 1: Not handling null input

// Wrong
val nestedJson = jsonDecoder.decodeFromString<NestedJson>(json)

// Correct
if (json != null) {
    val nestedJson = jsonDecoder.decodeFromString<NestedJson>(json)
    // ...
}

Mistake 2: Not handling invalid input

// Wrong
val nestedJson = jsonDecoder.decodeFromString<NestedJson>(json)

// Correct
try {
    val nestedJson = jsonDecoder.decodeFromString<NestedJson>(json)
    // ...
} catch (e: SerializationException) {
    println("Invalid input: ${e.message}")
}

Mistake 3: Not using a streaming parser for large input

// Wrong
val nestedJson = jsonDecoder.decodeFromString<NestedJson>(json)

// Correct
val jsonReader = JsonReader(json)
val nestedJson = jsonReader.decode<NestedJson>()

Performance Tips

Here are some performance tips for flattening nested JSON in Kotlin:

  1. Use a streaming JSON parser for large input to avoid loading the entire JSON into memory.
  2. Use a JSON decoder that supports Unicode characters to avoid encoding issues.
  3. Use the ignoreUnknownKeys option to ignore unexpected properties in the JSON input.

FAQ

Q: How do I handle nested arrays in JSON?

A: You can use the kotlinx.serialization library's Json decoder to decode nested arrays. For example:

data class NestedJson(val name: String, val addresses: List<Address>)

val nestedJson = jsonDecoder.decodeFromString<NestedJson>(json)

Q: How do I handle JSON with multiple levels of nesting?

A: You can use the kotlinx.serialization library's Json decoder to decode JSON with multiple levels of nesting. For example:

data class NestedJson(val name: String, val address: Address)
data class Address(val street: String, val city: City)
data class City(val name: String, val state: String)

val nestedJson = jsonDecoder.decodeFromString<NestedJson>(json)

Q: How do I handle JSON with optional properties?

A: You can use the kotlinx.serialization library's Json decoder to decode JSON with optional properties. For example:

data class NestedJson(val name: String, val address: Address?)

Q: How do I handle JSON with default values?

A: You can use the kotlinx.serialization library's Json decoder to decode JSON with default values. For example:

data class NestedJson(val name: String = "", val address: Address)

Q: How do I handle JSON with custom serialization?

A: You can use the kotlinx.serialization library's Json decoder to decode JSON with custom serialization. For example:

data class NestedJson(val name: String, val address: Address) {
    @Serializer
    fun serialize(encoder: Encoder) {
        // custom serialization logic
    }
}

AI agent tools available. The CodeTidy MCP Server gives Claude, Cursor, and other AI agents access to 60+ developer tools. One command: npx @codetidy/mcp