diff --git a/qs-kotlin/src/main/kotlin/io/github/techouse/qskotlin/internal/Decoder.kt b/qs-kotlin/src/main/kotlin/io/github/techouse/qskotlin/internal/Decoder.kt index ac536e0..93f4bc6 100644 --- a/qs-kotlin/src/main/kotlin/io/github/techouse/qskotlin/internal/Decoder.kt +++ b/qs-kotlin/src/main/kotlin/io/github/techouse/qskotlin/internal/Decoder.kt @@ -198,45 +198,42 @@ internal object Decoder { else -> Utils.combine(emptyList(), leaf) } } else { - val mutableObj = LinkedHashMap(1) + // Always build *string-keyed* maps here + val mutableObj = LinkedHashMap(1) + val cleanRoot = if (root.startsWith("[") && root.endsWith("]")) { root.substring(1, root.length - 1) } else root val decodedRoot = - if (options.getDecodeDotInKeys) { - cleanRoot.replace("%2E", ".") - } else cleanRoot + if (options.getDecodeDotInKeys) cleanRoot.replace("%2E", ".") else cleanRoot - val index: Int? = - if (decodedRoot.isNotEmpty() && decodedRoot.toIntOrNull() != null) - decodedRoot.toInt() - else null + val isPureNumeric = decodedRoot.isNotEmpty() && decodedRoot.all { it.isDigit() } + val idx: Int? = if (isPureNumeric) decodedRoot.toInt() else null + val isBracketedNumeric = + idx != null && root != decodedRoot && idx.toString() == decodedRoot when { - !options.parseLists && decodedRoot == "" -> { - mutableObj[0] = leaf + // If list parsing is disabled OR listLimit < 0: always make a map with string + // key + !options.parseLists || options.listLimit < 0 -> { + val keyForMap = if (decodedRoot == "") "0" else decodedRoot + mutableObj[keyForMap] = leaf obj = mutableObj } - index != null && - index >= 0 && - root != decodedRoot && - index.toString() == decodedRoot && - options.parseLists && - index <= options.listLimit -> { - val list = MutableList(index + 1) { Undefined.Companion() } - list[index] = leaf + // Proper list index (e.g., "[3]") and allowed by listLimit -> build a list + isBracketedNumeric && idx >= 0 && idx <= options.listLimit -> { + val list = MutableList(idx + 1) { Undefined.Companion() } + list[idx] = leaf obj = list } + // Otherwise, treat it as a map with *string* key (even if numeric) else -> { - if (index != null) { - mutableObj[index] = leaf - } else { - mutableObj[decodedRoot] = leaf - } + val keyForMap = decodedRoot + mutableObj[keyForMap] = leaf obj = mutableObj } } diff --git a/qs-kotlin/src/main/kotlin/io/github/techouse/qskotlin/internal/Encoder.kt b/qs-kotlin/src/main/kotlin/io/github/techouse/qskotlin/internal/Encoder.kt index 0f402d3..7c9cfa2 100644 --- a/qs-kotlin/src/main/kotlin/io/github/techouse/qskotlin/internal/Encoder.kt +++ b/qs-kotlin/src/main/kotlin/io/github/techouse/qskotlin/internal/Encoder.kt @@ -4,20 +4,12 @@ import io.github.techouse.qskotlin.enums.Format import io.github.techouse.qskotlin.enums.Formatter import io.github.techouse.qskotlin.enums.ListFormat import io.github.techouse.qskotlin.enums.ListFormatGenerator -import io.github.techouse.qskotlin.models.DateSerializer -import io.github.techouse.qskotlin.models.Filter -import io.github.techouse.qskotlin.models.FunctionFilter -import io.github.techouse.qskotlin.models.IterableFilter -import io.github.techouse.qskotlin.models.Sorter -import io.github.techouse.qskotlin.models.Undefined -import io.github.techouse.qskotlin.models.ValueEncoder -import io.github.techouse.qskotlin.models.WeakWrapper +import io.github.techouse.qskotlin.models.* import java.nio.charset.Charset import java.nio.charset.StandardCharsets import java.time.Instant import java.time.LocalDateTime -import java.util.WeakHashMap -import kotlin.collections.get +import java.util.* /** A helper object for encoding data into a query string format. */ internal object Encoder { diff --git a/qs-kotlin/src/main/kotlin/io/github/techouse/qskotlin/internal/Utils.kt b/qs-kotlin/src/main/kotlin/io/github/techouse/qskotlin/internal/Utils.kt index cc59427..45ad6e9 100644 --- a/qs-kotlin/src/main/kotlin/io/github/techouse/qskotlin/internal/Utils.kt +++ b/qs-kotlin/src/main/kotlin/io/github/techouse/qskotlin/internal/Utils.kt @@ -37,18 +37,21 @@ internal object Utils { is Iterable<*> -> when { target.any { it is Undefined } -> { - val mutableTarget: MutableMap = - target.withIndex().associate { it.index to it.value }.toMutableMap() + val mutableTarget: MutableMap = + target + .withIndex() + .associate { it.index.toString() to it.value } + .toMutableMap() when (source) { is Iterable<*> -> source.forEachIndexed { i, item -> if (item !is Undefined) { - mutableTarget[i] = item + mutableTarget[i.toString()] = item } } - else -> mutableTarget[mutableTarget.size] = source + else -> mutableTarget[mutableTarget.size.toString()] = source } when { @@ -117,7 +120,7 @@ internal object Utils { is Iterable<*> -> { source.forEachIndexed { i, item -> if (item !is Undefined) { - mutableTarget[i] = item + mutableTarget[i.toString()] = item } } } @@ -146,16 +149,16 @@ internal object Utils { if (target == null || target !is Map<*, *>) { return when (target) { is Iterable<*> -> { - val mutableTarget: MutableMap = + val mutableTarget: MutableMap = target .withIndex() - .associate { it.index to it.value } + .associate { it.index.toString() to it.value } .filterValues { it !is Undefined } .toMutableMap() @Suppress("UNCHECKED_CAST") (source as Map).forEach { (key, value) -> - mutableTarget[key] = value + mutableTarget[key.toString()] = value } mutableTarget } @@ -183,10 +186,9 @@ internal object Utils { target is Iterable<*> && source !is Iterable<*> -> target .withIndex() - .associate { it.index to it.value } + .associate { it.index.toString() to it.value } .filterValues { it !is Undefined } .toMutableMap() - else -> (target as Map).toMutableMap() } diff --git a/qs-kotlin/src/test/kotlin/io/github/techouse/qskotlin/fixtures/data/EmptyTestCases.kt b/qs-kotlin/src/test/kotlin/io/github/techouse/qskotlin/fixtures/data/EmptyTestCases.kt index 47b5a9d..edb62a0 100644 --- a/qs-kotlin/src/test/kotlin/io/github/techouse/qskotlin/fixtures/data/EmptyTestCases.kt +++ b/qs-kotlin/src/test/kotlin/io/github/techouse/qskotlin/fixtures/data/EmptyTestCases.kt @@ -178,12 +178,12 @@ internal val EmptyTestCases: List> = "indices" to "[0]=a&[1]=b& [0]=1", "repeat" to "=a&=b& =1", ), - "noEmptyKeys" to mapOf(0 to "a", 1 to "b", " " to listOf("1")), + "noEmptyKeys" to mapOf("0" to "a", "1" to "b", " " to listOf("1")), ), mapOf( "input" to "[0]=a&[1]=b&a[0]=1&a[1]=2", "withEmptyKeys" to mapOf("" to listOf("a", "b"), "a" to listOf("1", "2")), - "noEmptyKeys" to mapOf(0 to "a", 1 to "b", "a" to listOf("1", "2")), + "noEmptyKeys" to mapOf("0" to "a", "1" to "b", "a" to listOf("1", "2")), "stringifyOutput" to mapOf( "brackets" to "[]=a&[]=b&a[]=1&a[]=2", @@ -207,6 +207,6 @@ internal val EmptyTestCases: List> = "withEmptyKeys" to mapOf("" to listOf("a", "b")), "stringifyOutput" to mapOf("brackets" to "[]=a&[]=b", "indices" to "[0]=a&[1]=b", "repeat" to "=a&=b"), - "noEmptyKeys" to mapOf(0 to "a", 1 to "b"), + "noEmptyKeys" to mapOf("0" to "a", "1" to "b"), ), ) diff --git a/qs-kotlin/src/test/kotlin/io/github/techouse/qskotlin/unit/DecodeSpec.kt b/qs-kotlin/src/test/kotlin/io/github/techouse/qskotlin/unit/DecodeSpec.kt index 7a85ee2..a69daf5 100644 --- a/qs-kotlin/src/test/kotlin/io/github/techouse/qskotlin/unit/DecodeSpec.kt +++ b/qs-kotlin/src/test/kotlin/io/github/techouse/qskotlin/unit/DecodeSpec.kt @@ -74,7 +74,7 @@ class DecodeSpec : } it("parses a simple string") { - decode("0=foo") shouldBe mapOf(0 to "foo") + decode("0=foo") shouldBe mapOf("0" to "foo") decode("foo=c++") shouldBe mapOf("foo" to "c ") decode("a[>=]=23") shouldBe mapOf("a" to mapOf(">=" to "23")) decode("a[<=>]==23") shouldBe mapOf("a" to mapOf("<=>" to "=23")) @@ -304,14 +304,14 @@ class DecodeSpec : decode("a[1]=c&a[0]=b") shouldBe mapOf("a" to listOf("b", "c")) decode("a[1]=c", DecodeOptions(listLimit = 20)) shouldBe mapOf("a" to listOf("c")) decode("a[1]=c", DecodeOptions(listLimit = 0)) shouldBe - mapOf("a" to mapOf(1 to "c")) + mapOf("a" to mapOf("1" to "c")) decode("a[1]=c") shouldBe mapOf("a" to listOf("c")) decode("a[0]=b&a[2]=c", DecodeOptions(parseLists = false)) shouldBe - mapOf("a" to mapOf(0 to "b", 2 to "c")) + mapOf("a" to mapOf("0" to "b", "2" to "c")) decode("a[0]=b&a[2]=c", DecodeOptions(parseLists = true)) shouldBe mapOf("a" to listOf("b", "c")) decode("a[1]=b&a[15]=c", DecodeOptions(parseLists = false)) shouldBe - mapOf("a" to mapOf(1 to "b", 15 to "c")) + mapOf("a" to mapOf("1" to "b", "15" to "c")) decode("a[1]=b&a[15]=c", DecodeOptions(parseLists = true)) shouldBe mapOf("a" to listOf("b", "c")) } @@ -319,10 +319,10 @@ class DecodeSpec : it("limits specific list indices to listLimit") { decode("a[20]=a", DecodeOptions(listLimit = 20)) shouldBe mapOf("a" to listOf("a")) decode("a[21]=a", DecodeOptions(listLimit = 20)) shouldBe - mapOf("a" to mapOf(21 to "a")) + mapOf("a" to mapOf("21" to "a")) decode("a[20]=a") shouldBe mapOf("a" to listOf("a")) - decode("a[21]=a") shouldBe mapOf("a" to mapOf(21 to "a")) + decode("a[21]=a") shouldBe mapOf("a" to mapOf("21" to "a")) } it("supports keys that begin with a number") { @@ -351,15 +351,15 @@ class DecodeSpec : it("transforms lists to maps") { decode("foo[0]=bar&foo[bad]=baz") shouldBe - mapOf("foo" to mapOf(0 to "bar", "bad" to "baz")) + mapOf("foo" to mapOf("0" to "bar", "bad" to "baz")) decode("foo[bad]=baz&foo[0]=bar") shouldBe - mapOf("foo" to mapOf("bad" to "baz", 0 to "bar")) + mapOf("foo" to mapOf("bad" to "baz", "0" to "bar")) decode("foo[bad]=baz&foo[]=bar") shouldBe - mapOf("foo" to mapOf("bad" to "baz", 0 to "bar")) + mapOf("foo" to mapOf("bad" to "baz", "0" to "bar")) decode("foo[]=bar&foo[bad]=baz") shouldBe - mapOf("foo" to mapOf(0 to "bar", "bad" to "baz")) + mapOf("foo" to mapOf("0" to "bar", "bad" to "baz")) decode("foo[bad]=baz&foo[]=bar&foo[]=foo") shouldBe - mapOf("foo" to mapOf("bad" to "baz", 0 to "bar", 1 to "foo")) + mapOf("foo" to mapOf("bad" to "baz", "0" to "bar", "1" to "foo")) decode("foo[0][a]=a&foo[0][b]=b&foo[1][a]=aa&foo[1][b]=bb") shouldBe mapOf( "foo" to @@ -387,13 +387,13 @@ class DecodeSpec : DecodeOptions(allowDots = true), ) shouldBe mapOf("foo" to listOf(mapOf("baz" to listOf("15", "16"), "bar" to "2"))) decode("foo.bad=baz&foo[0]=bar", DecodeOptions(allowDots = true)) shouldBe - mapOf("foo" to mapOf("bad" to "baz", 0 to "bar")) + mapOf("foo" to mapOf("bad" to "baz", "0" to "bar")) decode("foo.bad=baz&foo[]=bar", DecodeOptions(allowDots = true)) shouldBe - mapOf("foo" to mapOf("bad" to "baz", 0 to "bar")) + mapOf("foo" to mapOf("bad" to "baz", "0" to "bar")) decode("foo[]=bar&foo.bad=baz", DecodeOptions(allowDots = true)) shouldBe - mapOf("foo" to mapOf(0 to "bar", "bad" to "baz")) + mapOf("foo" to mapOf("0" to "bar", "bad" to "baz")) decode("foo.bad=baz&foo[]=bar&foo[]=foo", DecodeOptions(allowDots = true)) shouldBe - mapOf("foo" to mapOf("bad" to "baz", 0 to "bar", 1 to "foo")) + mapOf("foo" to mapOf("bad" to "baz", "0" to "bar", "1" to "foo")) decode( "foo[0].a=a&foo[0].b=b&foo[1].a=aa&foo[1].b=bb", DecodeOptions(allowDots = true), @@ -406,7 +406,7 @@ class DecodeSpec : it("correctly prunes undefined values when converting a list to a map") { decode("a[2]=b&a[99999999]=c") shouldBe - mapOf("a" to mapOf(2 to "b", 99999999 to "c")) + mapOf("a" to mapOf("2" to "b", "99999999" to "c")) } it("supports malformed uri characters") { @@ -482,9 +482,9 @@ class DecodeSpec : } it("continues parsing when no parent is found") { - decode("[]=&a=b") shouldBe mapOf(0 to "", "a" to "b") + decode("[]=&a=b") shouldBe mapOf("0" to "", "a" to "b") decode("[]&a=b", DecodeOptions(strictNullHandling = true)) shouldBe - mapOf(0 to null, "a" to "b") + mapOf("0" to null, "a" to "b") decode("[foo]=bar") shouldBe mapOf("foo" to "bar") } @@ -519,25 +519,25 @@ class DecodeSpec : it("allows overriding list limit") { decode("a[0]=b", DecodeOptions(listLimit = -1)) shouldBe - mapOf("a" to mapOf(0 to "b")) + mapOf("a" to mapOf("0" to "b")) decode("a[0]=b", DecodeOptions(listLimit = 0)) shouldBe mapOf("a" to listOf("b")) decode("a[-1]=b", DecodeOptions(listLimit = -1)) shouldBe - mapOf("a" to mapOf(-1 to "b")) + mapOf("a" to mapOf("-1" to "b")) decode("a[-1]=b", DecodeOptions(listLimit = 0)) shouldBe - mapOf("a" to mapOf(-1 to "b")) + mapOf("a" to mapOf("-1" to "b")) decode("a[0]=b&a[1]=c", DecodeOptions(listLimit = -1)) shouldBe - mapOf("a" to mapOf(0 to "b", 1 to "c")) + mapOf("a" to mapOf("0" to "b", "1" to "c")) decode("a[0]=b&a[1]=c", DecodeOptions(listLimit = 0)) shouldBe - mapOf("a" to mapOf(0 to "b", 1 to "c")) + mapOf("a" to mapOf("0" to "b", "1" to "c")) } it("allows disabling list parsing") { decode("a[0]=b&a[1]=c", DecodeOptions(parseLists = false)) shouldBe - mapOf("a" to mapOf(0 to "b", 1 to "c")) + mapOf("a" to mapOf("0" to "b", "1" to "c")) decode("a[]=b", DecodeOptions(parseLists = false)) shouldBe - mapOf("a" to mapOf(0 to "b")) + mapOf("a" to mapOf("0" to "b")) } it("allows for query string prefix") { @@ -725,7 +725,7 @@ class DecodeSpec : val expectedList = mutableMapOf() expectedList["a"] = mutableMapOf() - (expectedList["a"] as MutableMap)[0] = "b" + (expectedList["a"] as MutableMap)["0"] = "b" (expectedList["a"] as MutableMap)["c"] = "d" decode("a[]=b&a[c]=d") shouldBe expectedList } @@ -1032,7 +1032,17 @@ class DecodeSpec : "a[1]=1&a[2]=2&a[3]=3&a[4]=4&a[5]=5&a[6]=6", DecodeOptions(listLimit = 5), ) shouldBe - mapOf("a" to mapOf(1 to "1", 2 to "2", 3 to "3", 4 to "4", 5 to "5", 6 to "6")) + mapOf( + "a" to + mapOf( + "1" to "1", + "2" to "2", + "3" to "3", + "4" to "4", + "5" to "5", + "6" to "6", + ) + ) } it("handles list limit of zero correctly") { diff --git a/qs-kotlin/src/test/kotlin/io/github/techouse/qskotlin/unit/ExampleSpec.kt b/qs-kotlin/src/test/kotlin/io/github/techouse/qskotlin/unit/ExampleSpec.kt index c67c7e5..0eaf527 100644 --- a/qs-kotlin/src/test/kotlin/io/github/techouse/qskotlin/unit/ExampleSpec.kt +++ b/qs-kotlin/src/test/kotlin/io/github/techouse/qskotlin/unit/ExampleSpec.kt @@ -179,21 +179,21 @@ class ExampleSpec : } it("converts high indices to Map keys") { - decode("a[100]=b") shouldBe mapOf("a" to mapOf(100 to "b")) + decode("a[100]=b") shouldBe mapOf("a" to mapOf("100" to "b")) } it("can override list limit") { decode("a[1]=b", DecodeOptions(listLimit = 0)) shouldBe - mapOf("a" to mapOf(1 to "b")) + mapOf("a" to mapOf("1" to "b")) } it("can disable list parsing entirely") { decode("a[]=b", DecodeOptions(parseLists = false)) shouldBe - mapOf("a" to mapOf(0 to "b")) + mapOf("a" to mapOf("0" to "b")) } it("merges mixed notations into Map") { - decode("a[0]=b&a[b]=c") shouldBe mapOf("a" to mapOf(0 to "b", "b" to "c")) + decode("a[0]=b&a[b]=c") shouldBe mapOf("a" to mapOf("0" to "b", "b" to "c")) } it("can create lists of Maps") { diff --git a/qs-kotlin/src/test/kotlin/io/github/techouse/qskotlin/unit/QsParserSpec.kt b/qs-kotlin/src/test/kotlin/io/github/techouse/qskotlin/unit/QsParserSpec.kt index 35fc3e1..9500a03 100644 --- a/qs-kotlin/src/test/kotlin/io/github/techouse/qskotlin/unit/QsParserSpec.kt +++ b/qs-kotlin/src/test/kotlin/io/github/techouse/qskotlin/unit/QsParserSpec.kt @@ -21,7 +21,7 @@ class QsParserSpec : val options = DecodeOptions() val optionsStrictNullHandling = DecodeOptions(strictNullHandling = true) - decode("0=foo", options) shouldBe mapOf(0 to "foo") + decode("0=foo", options) shouldBe mapOf("0" to "foo") decode("foo=c++", options) shouldBe mapOf("foo" to "c ") @@ -172,7 +172,7 @@ class QsParserSpec : decode("a[1]=c", options20) shouldBe mapOf("a" to listOf("c")) - decode("a[1]=c", options0) shouldBe mapOf("a" to mapOf(1 to "c")) + decode("a[1]=c", options0) shouldBe mapOf("a" to mapOf("1" to "c")) decode("a[1]=c", options) shouldBe mapOf("a" to listOf("c")) } @@ -183,11 +183,11 @@ class QsParserSpec : decode("a[20]=a", options20) shouldBe mapOf("a" to listOf("a")) - decode("a[21]=a", options20) shouldBe mapOf("a" to mapOf(21 to "a")) + decode("a[21]=a", options20) shouldBe mapOf("a" to mapOf("21" to "a")) decode("a[20]=a", options) shouldBe mapOf("a" to listOf("a")) - decode("a[21]=a", options) shouldBe mapOf("a" to mapOf(21 to "a")) + decode("a[21]=a", options) shouldBe mapOf("a" to mapOf("21" to "a")) } it("should support keys that begin with a number") { @@ -231,19 +231,19 @@ class QsParserSpec : val options = DecodeOptions() decode("foo[0]=bar&foo[bad]=baz", options) shouldBe - mapOf("foo" to mapOf(0 to "bar", "bad" to "baz")) + mapOf("foo" to mapOf("0" to "bar", "bad" to "baz")) decode("foo[bad]=baz&foo[0]=bar", options) shouldBe - mapOf("foo" to mapOf("bad" to "baz", 0 to "bar")) + mapOf("foo" to mapOf("bad" to "baz", "0" to "bar")) decode("foo[bad]=baz&foo[]=bar", options) shouldBe - mapOf("foo" to mapOf("bad" to "baz", 0 to "bar")) + mapOf("foo" to mapOf("bad" to "baz", "0" to "bar")) decode("foo[]=bar&foo[bad]=baz", options) shouldBe - mapOf("foo" to mapOf(0 to "bar", "bad" to "baz")) + mapOf("foo" to mapOf("0" to "bar", "bad" to "baz")) decode("foo[bad]=baz&foo[]=bar&foo[]=foo", options) shouldBe - mapOf("foo" to mapOf("bad" to "baz", 0 to "bar", 1 to "foo")) + mapOf("foo" to mapOf("bad" to "baz", "0" to "bar", "1" to "foo")) decode("foo[0][a]=a&foo[0][b]=b&foo[1][a]=aa&foo[1][b]=bb", options) shouldBe mapOf( @@ -277,16 +277,16 @@ class QsParserSpec : mapOf("foo" to listOf(mapOf("baz" to listOf("15", "16"), "bar" to "2"))) decode("foo.bad=baz&foo[0]=bar", optionsAllowDots) shouldBe - mapOf("foo" to mapOf("bad" to "baz", 0 to "bar")) + mapOf("foo" to mapOf("bad" to "baz", "0" to "bar")) decode("foo.bad=baz&foo[]=bar", optionsAllowDots) shouldBe - mapOf("foo" to mapOf("bad" to "baz", 0 to "bar")) + mapOf("foo" to mapOf("bad" to "baz", "0" to "bar")) decode("foo[]=bar&foo.bad=baz", optionsAllowDots) shouldBe - mapOf("foo" to mapOf(0 to "bar", "bad" to "baz")) + mapOf("foo" to mapOf("0" to "bar", "bad" to "baz")) decode("foo.bad=baz&foo[]=bar&foo[]=foo", optionsAllowDots) shouldBe - mapOf("foo" to mapOf("bad" to "baz", 0 to "bar", 1 to "foo")) + mapOf("foo" to mapOf("bad" to "baz", "0" to "bar", "1" to "foo")) decode("foo[0].a=a&foo[0].b=b&foo[1].a=aa&foo[1].b=bb", optionsAllowDots) shouldBe mapOf( @@ -299,7 +299,7 @@ class QsParserSpec : val options = DecodeOptions() decode("a[2]=b&a[99999999]=c", options) shouldBe - mapOf("a" to mapOf(2 to "b", 99999999 to "c")) + mapOf("a" to mapOf("2" to "b", "99999999" to "c")) } it("should support malformed URI characters") { @@ -416,9 +416,9 @@ class QsParserSpec : val options = DecodeOptions() val optionsStrictNullHandling = DecodeOptions(strictNullHandling = true) - decode("[]=&a=b", options) shouldBe mapOf(0 to "", "a" to "b") + decode("[]=&a=b", options) shouldBe mapOf("0" to "", "a" to "b") - decode("[]&a=b", optionsStrictNullHandling) shouldBe mapOf(0 to null, "a" to "b") + decode("[]&a=b", optionsStrictNullHandling) shouldBe mapOf("0" to null, "a" to "b") decode("[foo]=bar", optionsStrictNullHandling) shouldBe mapOf("foo" to "bar") } @@ -455,20 +455,21 @@ class QsParserSpec : it("should allow overriding list limit") { val optionsNegative = DecodeOptions(listLimit = -1) - decode("a[0]=b", optionsNegative) shouldBe mapOf("a" to mapOf(0 to "b")) + decode("a[0]=b", optionsNegative) shouldBe mapOf("a" to mapOf("0" to "b")) - decode("a[-1]=b", optionsNegative) shouldBe mapOf("a" to mapOf(-1 to "b")) + decode("a[-1]=b", optionsNegative) shouldBe mapOf("a" to mapOf("-1" to "b")) decode("a[0]=b&a[1]=c", optionsNegative) shouldBe - mapOf("a" to mapOf(0 to "b", 1 to "c")) + mapOf("a" to mapOf("0" to "b", "1" to "c")) } it("should allow disabling list parsing") { val options = DecodeOptions(parseLists = false) - decode("a[0]=b&a[1]=c", options) shouldBe mapOf("a" to mapOf(0 to "b", 1 to "c")) + decode("a[0]=b&a[1]=c", options) shouldBe + mapOf("a" to mapOf("0" to "b", "1" to "c")) - decode("a[]=b", options) shouldBe mapOf("a" to mapOf(0 to "b")) + decode("a[]=b", options) shouldBe mapOf("a" to mapOf("0" to "b")) } it("should allow for query string prefix") { @@ -1009,7 +1010,8 @@ class QsParserSpec : val expected1 = mapOf( - "foo" to mapOf(0 to "bar", 1 to mapOf("first" to "123"), "second" to "456") + "foo" to + mapOf("0" to "bar", "1" to mapOf("first" to "123"), "second" to "456") ) Utils.merge(d1, d2) shouldBe expected1 diff --git a/qs-kotlin/src/test/kotlin/io/github/techouse/qskotlin/unit/UtilsSpec.kt b/qs-kotlin/src/test/kotlin/io/github/techouse/qskotlin/unit/UtilsSpec.kt index 8dc8ab2..b125102 100644 --- a/qs-kotlin/src/test/kotlin/io/github/techouse/qskotlin/unit/UtilsSpec.kt +++ b/qs-kotlin/src/test/kotlin/io/github/techouse/qskotlin/unit/UtilsSpec.kt @@ -370,7 +370,7 @@ class UtilsSpec : context("Utils.merge") { test("merges Map with List") { Utils.merge(mapOf(0 to "a"), listOf(Undefined(), "b")) shouldBe - mapOf(0 to "a", 1 to "b") + mapOf(0 to "a", "1" to "b") } test("merges two objects with the same key and different values") { @@ -438,7 +438,8 @@ class UtilsSpec : mapOf("foo" to mapOf("second" to "456")), ) shouldBe mapOf( - "foo" to mapOf(0 to "bar", 1 to mapOf("first" to "123"), "second" to "456") + "foo" to + mapOf("0" to "bar", "1" to mapOf("first" to "123"), "second" to "456") ) } @@ -520,7 +521,7 @@ class UtilsSpec : mapOf("foo" to listOf("bar")), mapOf("foo" to mapOf("baz" to "xyzzy")), ) - result shouldBe mapOf("foo" to mapOf(0 to "bar", "baz" to "xyzzy")) + result shouldBe mapOf("foo" to mapOf("0" to "bar", "baz" to "xyzzy")) @Suppress("UNCHECKED_CAST") val map = result as Map map.shouldContainKey("foo") @@ -533,7 +534,7 @@ class UtilsSpec : mapOf("foo" to mapOf("bar" to "baz")), mapOf("foo" to listOf("xyzzy")), ) - result shouldBe mapOf("foo" to mapOf("bar" to "baz", 0 to "xyzzy")) + result shouldBe mapOf("foo" to mapOf("bar" to "baz", "0" to "xyzzy")) @Suppress("UNCHECKED_CAST") val map = result as Map map.shouldContainKey("foo")