Show how to construct ad-hoc JSON with the buildJsonObject {} and buildJsonArray {} DSLs. What builder functions are available inside the block?
answer
- buildJsonObject { put(...) }, buildJsonArray { add(...) }
- Nesting: putJsonObject / putJsonArray; addJsonObject / addJsonArray
- put overloads: String/Int/Long/Double/Boolean/JsonElement
- Result is immutable JsonObject/JsonArray
- Same type-safe-builder pattern as buildList
basics
~10 sbuildJsonObject { } lets you assemble a JSON object by calling put("key", value) for primitives, and putJsonObject/putJsonArray for nested structures. buildJsonArray { } builds arrays with add(...). They return immutable JsonObject/JsonArray.
solid answer
~30 s`buildJsonObject { }` and `buildJsonArray { }` are DSL builders (in `kotlinx.serialization.json`) for creating JSON trees programmatically without a backing class. Inside `buildJsonObject` the receiver is a `JsonObjectBuilder`, exposing `put(key, String/Int/Long/Double/Boolean/JsonElement)`, the nesting helpers `putJsonObject(key) { }` and `putJsonArray(key) { }`, and `putJsonObject`/raw `put` for a pre-built element. Inside `buildJsonArray` the receiver is a `JsonArrayBuilder` with `add(...)`, `addJsonObject { }`, `addJsonArray { }`, and `addAll(...)`. Both builders return **immutable** `JsonObject`/`JsonArray`. This is ideal for shaping request bodies, test fixtures, or merging dynamic fields. You can then serialise with `Json.encodeToString(JsonElement.serializer(), tree)` or `tree.toString()`.
code
kotlin · 7 linesimport kotlinx.serialization.json.*
val tree = buildJsonArray {
addJsonObject { put("id", 1); put("name", "a") }
addJsonObject { put("id", 2); put("name", "b") }
}
println(tree) // [{"id":1,"name":"a"},{"id":2,"name":"b"}]go deeper
Can call put for primitives and produce a flat object.
Uses putJsonObject/putJsonArray and add* for nesting and knows the result is immutable.
Explains when to hand-build vs encodeToJsonElement, and how to rebuild for edits cleanly.
Weighs DSL ergonomics vs typed models at API boundaries and standardises a pattern for dynamic payload assembly across a codebase.
## The two builder DSLs kotlinx.serialization ships two top-level builder functions in `kotlinx.serialization.json`: - `buildJsonObject { }` → returns an immutable `JsonObject` - `buildJsonArray { }` → returns an immutable `JsonArray` They are **type-safe builders** (the same Kotlin DSL pattern used by `buildList`/`buildString`): the lambda has a *receiver* object whose members you call to add entries. ## Inside buildJsonObject — the JsonObjectBuilder receiver - **`put(key, value)`** — overloads accept `String`, `Number` (`Int`, `Long`, `Double`), `Boolean`, and `JsonElement`. There's also `put(key, null as String?)` style and a `JsonNull` overload. - **`putJsonObject(key) { ... }`** — nests another object built by an inner block. - **`putJsonArray(key) { ... }`** — nests an array. ## Inside buildJsonArray — the JsonArrayBuilder receiver - **`add(value)`** — `String`/`Number`/`Boolean`/`JsonElement` overloads. - **`addJsonObject { ... }`** and **`addJsonArray { ... }`** — nested structures. - **`addAll(collection)`** — bulk add. ## Example ```kotlin import kotlinx.serialization.json.* val payload = buildJsonObject { put("name", "Ada") put("age", 36) put("active", true) putJsonArray("langs") { add("Kotlin") add("Java") } putJsonObject("address") { put("city", "London") } } // {"name":"Ada","age":36,"active":true,"langs":["Kotlin","Java"],"address":{"city":"London"}} val text = payload.toString() // or Json.encodeToString(payload) ``` ## Immutability The returned `JsonObject`/`JsonArray` are **read-only** snapshots. To "modify" one you build a new tree (e.g. `buildJsonObject { original.forEach { (k, v) -> put(k, v) }; put("extra", 1) }`). There is no in-place mutation. ## When to use it Great for request bodies whose shape is dynamic, test fixtures, or assembling responses field-by-field. For a stable schema, prefer `encodeToJsonElement(value)` on a typed object instead of hand-building.
- Can you mutate a JsonObject after building it?No. It's an immutable read-only snapshot (a Map view). You rebuild a new tree to add or change fields.
- How do you add an already-built JsonElement under a key?Use the put(key, element) overload that accepts a JsonElement directly inside buildJsonObject.
saying these in an interview costs you the question
- Trying to mutate the result with obj["k"] = v (it's read-only)
- Inventing methods like set() or addObject() instead of put/add/putJsonObject
- Thinking you must wrap primitives manually as JsonPrimitive for every put (overloads exist)
- Confusing buildJsonObject (returns JsonObject) with Json {} (the configuration builder)