diff --git a/formats/json/commonMain/src/kotlinx/serialization/json/internal/JsonPath.kt b/formats/json/commonMain/src/kotlinx/serialization/json/internal/JsonPath.kt index 6c1372950..3ca26cc47 100644 --- a/formats/json/commonMain/src/kotlinx/serialization/json/internal/JsonPath.kt +++ b/formats/json/commonMain/src/kotlinx/serialization/json/internal/JsonPath.kt @@ -2,6 +2,7 @@ package kotlinx.serialization.json.internal import kotlinx.serialization.* import kotlinx.serialization.descriptors.* +import kotlinx.serialization.internal.* /** * Internal representation of the current JSON path. @@ -20,6 +21,9 @@ import kotlinx.serialization.descriptors.* * ``` */ internal class JsonPath { + + // Tombstone indicates that we are within a map, but the map key is currently being decoded. + // It is also used to overwrite a previous map key to avoid memory leaks and misattribution. object Tombstone /* @@ -68,6 +72,7 @@ internal class JsonPath { indicies[currentDepth] = -2 } + /** Used to indicate that we are in the process of decoding the key itself and can't specify it in path */ fun resetCurrentMapKey() { if (indicies[currentDepth] == -2) { currentObjectPath[currentDepth] = Tombstone @@ -111,8 +116,13 @@ internal class JsonPath { } } else if (element !== Tombstone) { append("[") - // Else -- structured key + // With this check numbers keys will be quoted, + // so both [42] and ['42'] are now non ambiguous + val isNumberKey = element is Number + if (isNumberKey) append("'") + // Else -- map key append(element) + if (isNumberKey) append("'") append("]") } } diff --git a/formats/json/commonTest/src/kotlinx/serialization/JsonPathTest.kt b/formats/json/commonTest/src/kotlinx/serialization/JsonPathTest.kt index 228e0a736..2f8db8351 100644 --- a/formats/json/commonTest/src/kotlinx/serialization/JsonPathTest.kt +++ b/formats/json/commonTest/src/kotlinx/serialization/JsonPathTest.kt @@ -29,7 +29,7 @@ class JsonPathTest : JsonTestBase() { @Test fun testMissingKey() { - expectPath("$.i.d[1]") { Json.decodeFromString("""{"a":42, "i":{"d":{1:{}}""") } + expectPath("$.i.d['1']") { Json.decodeFromString("""{"a":42, "i":{"d":{1:{}}""") } } @Test @@ -76,9 +76,9 @@ class JsonPathTest : JsonTestBase() { @Test fun testMapValue() { - expectPath("$.i.d[42]\n") { Json.decodeFromString("""{"a":42, "i":{ "d": {42: {"xx":"bar"}}""") } - expectPath("$.i.d[43]\n") { Json.decodeFromString("""{"a":42, "i":{ "d": {42: {"s":"s"}, 43: {"xx":"bar"}}}""") } - expectPath("$[239]") { Json.decodeFromString>("""{239:bar}""") } + expectPath("$.i.d['42']\n") { Json.decodeFromString("""{"a":42, "i":{ "d": {42: {"xx":"bar"}}""") } + expectPath("$.i.d['43']\n") { Json.decodeFromString("""{"a":42, "i":{ "d": {42: {"s":"s"}, 43: {"xx":"bar"}}}""") } + expectPath("$['239']") { Json.decodeFromString>("""{239:bar}""") } } @Serializable