You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何高效对kotlinx.serialization.json.JsonObject执行增、删、改原语属性操作?

Efficiently Modifying JsonObject (kotlinx.serialization)

Hey there! Let's break down how to efficiently add, update, and delete primitive properties in an existing JsonObject—especially considering its underlying immutable content map and nested structures.

Key Background

First, remember that JsonObject is immutable: its private content: Map<String, JsonElement> can't be modified directly. Any "change" will create a new JsonObject instance, but we can optimize this to avoid unnecessary copies.


1. Top-Level Primitive Operations

For properties directly on the root JsonObject, these operations are straightforward and efficient (leveraging Kotlin's optimized map operators):

Add a Top-Level Primitive

val originalObj: JsonObject = ...
val newObj = JsonObject(originalObj.content + ("newKey" to JsonPrimitive("newValue")))

The + operator creates a new map with the added key-value pair—no full copy of the original 50-property map is needed (hash map operations are amortized O(1)).

Update a Top-Level Primitive

Same as adding: the + operator overwrites existing keys automatically.

val updatedObj = JsonObject(originalObj.content + ("existingKey" to JsonPrimitive("updatedValue")))

Delete a Top-Level Primitive

Use the - operator to remove the key from the map:

val deletedObj = JsonObject(originalObj.content - "keyToRemove")

2. Nested Primitive Operations

For properties inside nested JsonObjects (e.g., user.address.zipCode), we need to recursively update only the path of nodes that change—this avoids copying the entire nested structure.

Add/Update Nested Primitive

Create an extension function to handle recursive updates:

fun JsonObject.setNestedPrimitive(path: List<String>, value: JsonPrimitive): JsonObject {
    if (path.isEmpty()) return this
    
    val currentKey = path.first()
    val remainingPath = path.drop(1)
    val currentElement = this[currentKey]

    val newElement = when {
        // Reached the target path: set the primitive value
        remainingPath.isEmpty() -> value
        // Recurse into nested JsonObject
        currentElement is JsonObject -> currentElement.setNestedPrimitive(remainingPath, value)
        else -> throw IllegalArgumentException("Path segment '$currentKey' is not a JsonObject")
    }

    // Return new JsonObject with updated element
    return JsonObject(this.content + (currentKey to newElement))
}

Usage:

// Update "user.address.zipCode" to "90210"
val updatedNestedObj = originalObj.setNestedPrimitive(
    path = listOf("user", "address", "zipCode"),
    value = JsonPrimitive("90210")
)

Delete Nested Primitive

Another extension function for recursive deletion:

fun JsonObject.removeNestedPrimitive(path: List<String>): JsonObject {
    if (path.isEmpty()) return this
    
    val currentKey = path.first()
    val remainingPath = path.drop(1)
    val currentElement = this[currentKey] ?: return this // Key doesn't exist, return original

    val newElement = when {
        // Reached target path: mark for deletion
        remainingPath.isEmpty() -> null
        // Recurse into nested JsonObject
        currentElement is JsonObject -> currentElement.removeNestedPrimitive(remainingPath)
        else -> throw IllegalArgumentException("Path segment '$currentKey' is not a JsonObject")
    }

    return if (newElement == null) {
        // Remove the key entirely if we deleted its value
        JsonObject(this.content - currentKey)
    } else {
        // Update the nested element with its new state
        JsonObject(this.content + (currentKey to newElement))
    }
}

Usage:

// Delete "user.address.zipCode"
val deletedNestedObj = originalObj.removeNestedPrimitive(
    path = listOf("user", "address", "zipCode")
)

3. About JsonObjectBuilder

You’re right that JsonObjectBuilder doesn’t directly accept existing JsonObjects out of the box—but you can manually copy existing content into it:

val originalObj: JsonObject = ...
val newObj = buildJsonObject {
    // Copy all existing properties first
    originalObj.forEach { (key, value) -> put(key, value) }
    // Add/update/delete as needed
    put("newKey", JsonPrimitive("newValue"))
    put("existingKey", JsonPrimitive("updatedValue"))
    // To delete, just omit the key (or filter before copying)
}

Note: This approach copies all 50 properties, which is less efficient than the map-operator or recursive extension methods for single changes. Use it only if you need to make multiple unrelated changes in one go.


Why This Is Optimal

  • Minimal copying: For top-level changes, we only modify the root map. For nested changes, only the nodes along the target path are recreated—all other nested structures are reused.
  • Immutability benefits: Original JsonObjects stay intact, which is safer for concurrency and functional programming patterns.

内容的提问来源于stack exchange,提问作者Nikhilesh Patve

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.29 21:42:39