From 5f6fae2b7865528fe50e17ac6f990ac2dab5eb99 Mon Sep 17 00:00:00 2001 From: Klemen Tusar Date: Wed, 13 Aug 2025 11:53:45 +0200 Subject: [PATCH 1/3] :children_crossing: normalize dictionary keys to strings in Decoder and tests --- QsNet.Tests/DecodeTests.cs | 128 +++++++++++++++++++----------------- QsNet.Tests/ExampleTests.cs | 8 +-- QsNet.Tests/UtilsTests.cs | 16 ++--- QsNet/Internal/Decoder.cs | 38 +++++------ QsNet/Internal/Utils.cs | 6 +- 5 files changed, 100 insertions(+), 96 deletions(-) diff --git a/QsNet.Tests/DecodeTests.cs b/QsNet.Tests/DecodeTests.cs index 162f845..752c80e 100644 --- a/QsNet.Tests/DecodeTests.cs +++ b/QsNet.Tests/DecodeTests.cs @@ -96,7 +96,7 @@ public void Decode_ThrowsArgumentException_IfInputIsNotStringOrMap() [Fact] public void Decode_ParsesSimpleString() { - Qs.Decode("0=foo").Should().BeEquivalentTo(new Dictionary { [0] = "foo" }); + Qs.Decode("0=foo").Should().BeEquivalentTo(new Dictionary { ["0"] = "foo" }); Qs.Decode("foo=c++") .Should() @@ -818,7 +818,7 @@ public void Decode_AllowsSpecifyingListIndices() .BeEquivalentTo( new Dictionary { - ["a"] = new Dictionary { [1] = "c" } + ["a"] = new Dictionary { ["1"] = "c" } } ); @@ -831,7 +831,7 @@ public void Decode_AllowsSpecifyingListIndices() .BeEquivalentTo( new Dictionary { - ["a"] = new Dictionary { [0] = "b", [2] = "c" } + ["a"] = new Dictionary { ["0"] = "b", ["2"] = "c" } } ); @@ -849,7 +849,7 @@ public void Decode_AllowsSpecifyingListIndices() .BeEquivalentTo( new Dictionary { - ["a"] = new Dictionary { [1] = "b", [15] = "c" } + ["a"] = new Dictionary { ["1"] = "b", ["15"] = "c" } } ); @@ -875,7 +875,7 @@ public void Decode_LimitsSpecificListIndices_ToListLimit() .BeEquivalentTo( new Dictionary { - ["a"] = new Dictionary { [21] = "a" } + ["a"] = new Dictionary { ["21"] = "a" } } ); @@ -888,7 +888,7 @@ public void Decode_LimitsSpecificListIndices_ToListLimit() .BeEquivalentTo( new Dictionary { - ["a"] = new Dictionary { [21] = "a" } + ["a"] = new Dictionary { ["21"] = "a" } } ); } @@ -964,7 +964,7 @@ public void Decode_TransformsListsToMaps() .BeEquivalentTo( new Dictionary { - ["foo"] = new Dictionary { [0] = "bar", ["bad"] = "baz" } + ["foo"] = new Dictionary { ["0"] = "bar", ["bad"] = "baz" } } ); @@ -973,7 +973,7 @@ public void Decode_TransformsListsToMaps() .BeEquivalentTo( new Dictionary { - ["foo"] = new Dictionary { ["bad"] = "baz", [0] = "bar" } + ["foo"] = new Dictionary { ["bad"] = "baz", ["0"] = "bar" } } ); @@ -982,7 +982,7 @@ public void Decode_TransformsListsToMaps() .BeEquivalentTo( new Dictionary { - ["foo"] = new Dictionary { ["bad"] = "baz", [0] = "bar" } + ["foo"] = new Dictionary { ["bad"] = "baz", ["0"] = "bar" } } ); @@ -991,7 +991,7 @@ public void Decode_TransformsListsToMaps() .BeEquivalentTo( new Dictionary { - ["foo"] = new Dictionary { [0] = "bar", ["bad"] = "baz" } + ["foo"] = new Dictionary { ["0"] = "bar", ["bad"] = "baz" } } ); @@ -1003,8 +1003,8 @@ public void Decode_TransformsListsToMaps() ["foo"] = new Dictionary { ["bad"] = "baz", - [0] = "bar", - [1] = "foo" + ["0"] = "bar", + ["1"] = "foo" } } ); @@ -1108,7 +1108,7 @@ public void Decode_TransformsListsToMaps_DotNotation() .BeEquivalentTo( new Dictionary { - ["foo"] = new Dictionary { ["bad"] = "baz", [0] = "bar" } + ["foo"] = new Dictionary { ["bad"] = "baz", ["0"] = "bar" } } ); @@ -1117,7 +1117,7 @@ public void Decode_TransformsListsToMaps_DotNotation() .BeEquivalentTo( new Dictionary { - ["foo"] = new Dictionary { ["bad"] = "baz", [0] = "bar" } + ["foo"] = new Dictionary { ["bad"] = "baz", ["0"] = "bar" } } ); @@ -1126,7 +1126,7 @@ public void Decode_TransformsListsToMaps_DotNotation() .BeEquivalentTo( new Dictionary { - ["foo"] = new Dictionary { [0] = "bar", ["bad"] = "baz" } + ["foo"] = new Dictionary { ["0"] = "bar", ["bad"] = "baz" } } ); @@ -1138,8 +1138,8 @@ public void Decode_TransformsListsToMaps_DotNotation() ["foo"] = new Dictionary { ["bad"] = "baz", - [0] = "bar", - [1] = "foo" + ["0"] = "bar", + ["1"] = "foo" } } ); @@ -1169,7 +1169,7 @@ public void Decode_CorrectlyPrunesUndefinedValues_WhenConvertingListToMap() .BeEquivalentTo( new Dictionary { - ["a"] = new Dictionary { [2] = "b", [99999999] = "c" } + ["a"] = new Dictionary { ["2"] = "b", ["99999999"] = "c" } } ); } @@ -1411,11 +1411,11 @@ public void Decode_ContinuesParsingWhenNoParentIsFound() { Qs.Decode("[]=&a=b") .Should() - .BeEquivalentTo(new Dictionary { [0] = "", ["a"] = "b" }); + .BeEquivalentTo(new Dictionary { ["0"] = "", ["a"] = "b" }); Qs.Decode("[]&a=b", new DecodeOptions { StrictNullHandling = true }) .Should() - .BeEquivalentTo(new Dictionary { [0] = null, ["a"] = "b" }); + .BeEquivalentTo(new Dictionary { ["0"] = null, ["a"] = "b" }); Qs.Decode("[foo]=bar") .Should() @@ -1476,7 +1476,7 @@ public void Decode_AllowsOverridingListLimit() .BeEquivalentTo( new Dictionary { - ["a"] = new Dictionary { [0] = "b" } + ["a"] = new Dictionary { ["0"] = "b" } } ); @@ -1489,7 +1489,7 @@ public void Decode_AllowsOverridingListLimit() .BeEquivalentTo( new Dictionary { - ["a"] = new Dictionary { [-1] = "b" } + ["a"] = new Dictionary { ["-1"] = "b" } } ); @@ -1498,7 +1498,7 @@ public void Decode_AllowsOverridingListLimit() .BeEquivalentTo( new Dictionary { - ["a"] = new Dictionary { [-1] = "b" } + ["a"] = new Dictionary { ["-1"] = "b" } } ); @@ -1507,7 +1507,7 @@ public void Decode_AllowsOverridingListLimit() .BeEquivalentTo( new Dictionary { - ["a"] = new Dictionary { [0] = "b", [1] = "c" } + ["a"] = new Dictionary { ["0"] = "b", ["1"] = "c" } } ); @@ -1516,7 +1516,7 @@ public void Decode_AllowsOverridingListLimit() .BeEquivalentTo( new Dictionary { - ["a"] = new Dictionary { [0] = "b", [1] = "c" } + ["a"] = new Dictionary { ["0"] = "b", ["1"] = "c" } } ); } @@ -1529,7 +1529,7 @@ public void Decode_AllowsDisablingListParsing() .BeEquivalentTo( new Dictionary { - ["a"] = new Dictionary { [0] = "b", [1] = "c" } + ["a"] = new Dictionary { ["0"] = "b", ["1"] = "c" } } ); @@ -1538,7 +1538,7 @@ public void Decode_AllowsDisablingListParsing() .BeEquivalentTo( new Dictionary { - ["a"] = new Dictionary { [0] = "b" } + ["a"] = new Dictionary { ["0"] = "b" } } ); } @@ -1956,8 +1956,10 @@ public void Decode_AddKeysToMaps() [Fact] public void Decode_CanReturnNullMaps() { - var expected = new Dictionary(); - expected["a"] = new Dictionary(); + var expected = new Dictionary + { + ["a"] = new Dictionary() + }; ((Dictionary)expected["a"]!)["b"] = "c"; ((Dictionary)expected["a"]!)["hasOwnProperty"] = "d"; @@ -1965,9 +1967,11 @@ public void Decode_CanReturnNullMaps() Qs.Decode(null).Should().BeEquivalentTo(new Dictionary()); - var expectedList = new Dictionary(); - expectedList["a"] = new Dictionary(); - ((Dictionary)expectedList["a"]!)[0] = "b"; + var expectedList = new Dictionary + { + ["a"] = new Dictionary() + }; + ((Dictionary)expectedList["a"]!)["0"] = "b"; ((Dictionary)expectedList["a"]!)["c"] = "d"; Qs.Decode("a[]=b&a[c]=d").Should().BeEquivalentTo(expectedList); @@ -2491,12 +2495,12 @@ public void Decode_ListLimit_ConvertsListToMapIfLengthIsGreaterThanLimit() { ["a"] = new Dictionary { - [1] = "1", - [2] = "2", - [3] = "3", - [4] = "4", - [5] = "5", - [6] = "6" + ["1"] = "1", + ["2"] = "2", + ["3"] = "3", + ["4"] = "4", + ["5"] = "5", + ["6"] = "6" } } ); @@ -2543,7 +2547,7 @@ public void ShouldParseASimpleString() Qs.Decode("0=foo", options) .Should() - .BeEquivalentTo(new Dictionary { [0] = "foo" }); + .BeEquivalentTo(new Dictionary { ["0"] = "foo" }); Qs.Decode("foo=c++", options) .Should() @@ -2951,7 +2955,7 @@ public void ShouldAllowSpecifyingArrayIndices() .BeEquivalentTo( new Dictionary { - ["a"] = new Dictionary { [1] = "c" } + ["a"] = new Dictionary { ["1"] = "c" } } ); @@ -2975,7 +2979,7 @@ public void ShouldLimitSpecificArrayIndicesToListLimit() .BeEquivalentTo( new Dictionary { - ["a"] = new Dictionary { [21] = "a" } + ["a"] = new Dictionary { ["21"] = "a" } } ); @@ -2988,7 +2992,7 @@ public void ShouldLimitSpecificArrayIndicesToListLimit() .BeEquivalentTo( new Dictionary { - ["a"] = new Dictionary { [21] = "a" } + ["a"] = new Dictionary { ["21"] = "a" } } ); } @@ -3076,7 +3080,7 @@ public void ShouldTransformArraysToObjects() .BeEquivalentTo( new Dictionary { - ["foo"] = new Dictionary { [0] = "bar", ["bad"] = "baz" } + ["foo"] = new Dictionary { ["0"] = "bar", ["bad"] = "baz" } } ); @@ -3085,7 +3089,7 @@ public void ShouldTransformArraysToObjects() .BeEquivalentTo( new Dictionary { - ["foo"] = new Dictionary { ["bad"] = "baz", [0] = "bar" } + ["foo"] = new Dictionary { ["bad"] = "baz", ["0"] = "bar" } } ); @@ -3094,7 +3098,7 @@ public void ShouldTransformArraysToObjects() .BeEquivalentTo( new Dictionary { - ["foo"] = new Dictionary { ["bad"] = "baz", [0] = "bar" } + ["foo"] = new Dictionary { ["bad"] = "baz", ["0"] = "bar" } } ); @@ -3103,7 +3107,7 @@ public void ShouldTransformArraysToObjects() .BeEquivalentTo( new Dictionary { - ["foo"] = new Dictionary { [0] = "bar", ["bad"] = "baz" } + ["foo"] = new Dictionary { ["0"] = "bar", ["bad"] = "baz" } } ); @@ -3115,8 +3119,8 @@ public void ShouldTransformArraysToObjects() ["foo"] = new Dictionary { ["bad"] = "baz", - [0] = "bar", - [1] = "foo" + ["0"] = "bar", + ["1"] = "foo" } } ); @@ -3219,7 +3223,7 @@ public void ShouldTransformArraysToObjectsWithDotNotation() .BeEquivalentTo( new Dictionary { - ["foo"] = new Dictionary { ["bad"] = "baz", [0] = "bar" } + ["foo"] = new Dictionary { ["bad"] = "baz", ["0"] = "bar" } } ); @@ -3228,7 +3232,7 @@ public void ShouldTransformArraysToObjectsWithDotNotation() .BeEquivalentTo( new Dictionary { - ["foo"] = new Dictionary { ["bad"] = "baz", [0] = "bar" } + ["foo"] = new Dictionary { ["bad"] = "baz", ["0"] = "bar" } } ); @@ -3237,7 +3241,7 @@ public void ShouldTransformArraysToObjectsWithDotNotation() .BeEquivalentTo( new Dictionary { - ["foo"] = new Dictionary { [0] = "bar", ["bad"] = "baz" } + ["foo"] = new Dictionary { ["0"] = "bar", ["bad"] = "baz" } } ); @@ -3249,8 +3253,8 @@ public void ShouldTransformArraysToObjectsWithDotNotation() ["foo"] = new Dictionary { ["bad"] = "baz", - [0] = "bar", - [1] = "foo" + ["0"] = "bar", + ["1"] = "foo" } } ); @@ -3279,7 +3283,7 @@ public void ShouldCorrectlyPruneUndefinedValues() .BeEquivalentTo( new Dictionary { - ["a"] = new Dictionary { [2] = "b", [99999999] = "c" } + ["a"] = new Dictionary { ["2"] = "b", ["99999999"] = "c" } } ); } @@ -3594,11 +3598,11 @@ public void ShouldContinueParsingWhenNoParentIsFound() Qs.Decode("[]=&a=b", options) .Should() - .BeEquivalentTo(new Dictionary { [0] = "", ["a"] = "b" }); + .BeEquivalentTo(new Dictionary { ["0"] = "", ["a"] = "b" }); Qs.Decode("[]&a=b", optionsStrictNullHandling) .Should() - .BeEquivalentTo(new Dictionary { [0] = null, ["a"] = "b" }); + .BeEquivalentTo(new Dictionary { ["0"] = null, ["a"] = "b" }); Qs.Decode("[foo]=bar", optionsStrictNullHandling) .Should() @@ -3658,7 +3662,7 @@ public void ShouldAllowOverridingListLimit() .BeEquivalentTo( new Dictionary { - ["a"] = new Dictionary { [0] = "b" } + ["a"] = new Dictionary { ["0"] = "b" } } ); @@ -3667,7 +3671,7 @@ public void ShouldAllowOverridingListLimit() .BeEquivalentTo( new Dictionary { - ["a"] = new Dictionary { [-1] = "b" } + ["a"] = new Dictionary { ["-1"] = "b" } } ); @@ -3676,7 +3680,7 @@ public void ShouldAllowOverridingListLimit() .BeEquivalentTo( new Dictionary { - ["a"] = new Dictionary { [0] = "b", [1] = "c" } + ["a"] = new Dictionary { ["0"] = "b", ["1"] = "c" } } ); } @@ -3691,7 +3695,7 @@ public void ShouldAllowDisablingListParsing() .BeEquivalentTo( new Dictionary { - ["a"] = new Dictionary { [0] = "b", [1] = "c" } + ["a"] = new Dictionary { ["0"] = "b", ["1"] = "c" } } ); @@ -3700,7 +3704,7 @@ public void ShouldAllowDisablingListParsing() .BeEquivalentTo( new Dictionary { - ["a"] = new Dictionary { [0] = "b" } + ["a"] = new Dictionary { ["0"] = "b" } } ); } @@ -4117,6 +4121,6 @@ public void Decode_NonGeneric_Hashtable_Is_Normalised() var decoded = Qs.Decode(raw); // Assert – keys are strings and values preserved - decoded.Should().Equal(new Dictionary { ["x"] = 1, [2] = "y" }); + decoded.Should().Equal(new Dictionary { ["x"] = 1, ["2"] = "y" }); } } \ No newline at end of file diff --git a/QsNet.Tests/ExampleTests.cs b/QsNet.Tests/ExampleTests.cs index bd939e5..7ef4247 100644 --- a/QsNet.Tests/ExampleTests.cs +++ b/QsNet.Tests/ExampleTests.cs @@ -365,7 +365,7 @@ public void Decoding_Lists_ConvertsHighIndicesToMapKeys() .BeEquivalentTo( new Dictionary { - ["a"] = new Dictionary { [100] = "b" } + ["a"] = new Dictionary { ["100"] = "b" } } ); } @@ -379,7 +379,7 @@ public void Decoding_Lists_CanOverrideListLimit() .BeEquivalentTo( new Dictionary { - ["a"] = new Dictionary { [1] = "b" } + ["a"] = new Dictionary { ["1"] = "b" } } ); } @@ -393,7 +393,7 @@ public void Decoding_Lists_CanDisableListParsingEntirely() .BeEquivalentTo( new Dictionary { - ["a"] = new Dictionary { [0] = "b" } + ["a"] = new Dictionary { ["0"] = "b" } } ); } @@ -407,7 +407,7 @@ public void Decoding_Lists_MergesMixedNotationsIntoMap() .BeEquivalentTo( new Dictionary { - ["a"] = new Dictionary { [0] = "b", ["b"] = "c" } + ["a"] = new Dictionary { ["0"] = "b", ["b"] = "c" } } ); } diff --git a/QsNet.Tests/UtilsTests.cs b/QsNet.Tests/UtilsTests.cs index bb79a0a..d83161e 100644 --- a/QsNet.Tests/UtilsTests.cs +++ b/QsNet.Tests/UtilsTests.cs @@ -512,9 +512,9 @@ public void Unescape_HandlesBadHexAfterPercent() [Fact] public void Merge_MapWithList() { - var target = new Dictionary { { 0, "a" } }; + var target = new Dictionary { { "0", "a" } }; var source = new List { Undefined.Create(), "b" }; - var expected = new Dictionary { { 0, "a" }, { 1, "b" } }; + var expected = new Dictionary { { "0", "a" }, { "1", "b" } }; Utils.Merge(target, source).Should().BeEquivalentTo(expected); } @@ -763,9 +763,9 @@ public void Merge_StandaloneAndTwoObjectsIntoList() "foo", new Dictionary { - { 0, "bar" }, + { "0", "bar" }, { - 1, + "1", new Dictionary { { "first", "123" } } }, { "second", "456" } @@ -1020,7 +1020,7 @@ public void Merge_ObjectIntoList() { { "foo", - new Dictionary { { 0, "bar" }, { "baz", "xyzzy" } } + new Dictionary { { "0", "bar" }, { "baz", "xyzzy" } } } }; @@ -1054,7 +1054,7 @@ public void Merge_ListIntoObject() { { "foo", - new Dictionary { { "bar", "baz" }, { 0, "xyzzy" } } + new Dictionary { { "bar", "baz" }, { "0", "xyzzy" } } } }; @@ -1518,9 +1518,9 @@ public void Merge_MapsAndArrays() "foo", new Dictionary { - { 0, "bar" }, + { "0", "bar" }, { - 1, + "1", new Dictionary { { "first", "123" } } }, { "second", "456" } diff --git a/QsNet/Internal/Decoder.cs b/QsNet/Internal/Decoder.cs index 1472412..bc60700 100644 --- a/QsNet/Internal/Decoder.cs +++ b/QsNet/Internal/Decoder.cs @@ -134,9 +134,8 @@ int currentListLength { key = options.GetDecoder(part[..pos], charset)?.ToString() ?? string.Empty; var currentLength = - obj.TryGetValue(key, out var val) && val is IList list - ? list.Count - : 0; + obj.TryGetValue(key, out var val) && val is IList list ? list.Count : 0; + value = Utils.Apply( ParseListValue(part[(pos + 1)..], options, currentLength), v => options.GetDecoder(v?.ToString(), charset) @@ -206,14 +205,13 @@ bool valuesParsed if (leaf is IDictionary id and not Dictionary) { - // Preserve identity for self-referencing maps (and avoid an - // unnecessary copy when the caller doesn’t care about key type). + // Preserve identity for self-referencing maps var selfRef = id is Dictionary strDict && strDict.Keys.Any(k => ReferenceEquals(strDict[k], strDict)); if (!selfRef) - leaf = Utils.ToObjectKeyedDictionary(id); // only when safe + leaf = Utils.ToObjectKeyedDictionary(id); } for (var i = chain.Count - 1; i >= 0; i--) @@ -239,21 +237,24 @@ bool valuesParsed ? cleanRoot.Replace("%2E", ".") : cleanRoot; - // Was this segment a *bracketed* numeric like "[1]"? + // Bracketed numeric like "[1]"? var isPureNumeric = int.TryParse(decodedRoot, out var idx) && !string.IsNullOrEmpty(decodedRoot); var isBracketedNumeric = isPureNumeric && root != decodedRoot && idx.ToString() == decodedRoot; - if (!options.ParseLists && decodedRoot == "") - // "[]" when arrays are disabled → treat as object with key 0 - obj = new Dictionary { [0] = leaf }; + if (!options.ParseLists || options.ListLimit < 0) + { + var keyForMap = decodedRoot == "" ? "0" : decodedRoot; + obj = new Dictionary { [keyForMap] = leaf }; + } else + { switch (isBracketedNumeric) { - case true when options.ParseLists && idx >= 0 && idx <= options.ListLimit: + case true when idx >= 0 && idx <= options.ListLimit: { - // Within list limit → build a list up to idx + // Build a list up to idx (0 is allowed when ListLimit == 0) var list = new List(); for (var j = 0; j <= idx; j++) list.Add(j == idx ? leaf : Undefined.Instance); @@ -261,15 +262,15 @@ bool valuesParsed break; } case true: - // Bracketed numeric but *not* a list (limit 0/too small or arrays disabled) → object-keyed map - obj = new Dictionary { [idx] = leaf }; + // Not a list (e.g., idx > ListLimit) → map with the string key (e.g., "2", "99999999") + obj = new Dictionary { [decodedRoot] = leaf }; break; default: - // Pure digits? use int key; otherwise keep string - object dictKey = isPureNumeric ? idx : decodedRoot; - obj = new Dictionary { [dictKey] = leaf }; + // Non-numeric or non-bracketed → map with string key + obj = new Dictionary { [decodedRoot] = leaf }; break; } + } } leaf = obj; @@ -350,8 +351,7 @@ bool strictDepth open = key.IndexOf('[', close + 1); } - if (open < 0) - return segments; + if (open < 0) return segments; // When depth > 0, strictDepth can apply to the remainder. if (strictDepth) throw new IndexOutOfRangeException( diff --git a/QsNet/Internal/Utils.cs b/QsNet/Internal/Utils.cs index eaa13ed..e124c21 100644 --- a/QsNet/Internal/Utils.cs +++ b/QsNet/Internal/Utils.cs @@ -142,7 +142,7 @@ internal static partial class Utils foreach (var item in srcIter) { if (item is not Undefined) - mutable[i] = item; + mutable[i.ToString()] = item; i++; } @@ -185,7 +185,7 @@ internal static partial class Utils foreach (var v in tenum) { if (v is not Undefined) - dict[i] = v; + dict[i.ToString()] = v; i++; } @@ -201,7 +201,7 @@ internal static partial class Utils var list = new List { target, - ToStringKeyedDictionary((IDictionary)source) + ToObjectKeyedDictionary((IDictionary)source) }; return list; } From 38ecca966c3cc362c38e405c915da34bf33146d5 Mon Sep 17 00:00:00 2001 From: Klemen Tusar Date: Wed, 13 Aug 2025 12:26:40 +0200 Subject: [PATCH 2/3] :children_crossing: standardize all dictionary keys to `string` across Decoder and tests --- QsNet.Tests/DecodeTests.cs | 716 ++++++++++++++++----------------- QsNet.Tests/ExampleTests.cs | 110 ++--- QsNet.Tests/QsNet.Tests.csproj | 2 +- QsNet/Extensions.cs | 2 +- QsNet/Internal/Utils.cs | 157 +++++++- QsNet/Qs.cs | 11 +- 6 files changed, 575 insertions(+), 423 deletions(-) diff --git a/QsNet.Tests/DecodeTests.cs b/QsNet.Tests/DecodeTests.cs index 752c80e..8406fae 100644 --- a/QsNet.Tests/DecodeTests.cs +++ b/QsNet.Tests/DecodeTests.cs @@ -96,7 +96,7 @@ public void Decode_ThrowsArgumentException_IfInputIsNotStringOrMap() [Fact] public void Decode_ParsesSimpleString() { - Qs.Decode("0=foo").Should().BeEquivalentTo(new Dictionary { ["0"] = "foo" }); + Qs.Decode("0=foo").Should().BeEquivalentTo(new Dictionary { ["0"] = "foo" }); Qs.Decode("foo=c++") .Should() @@ -818,7 +818,7 @@ public void Decode_AllowsSpecifyingListIndices() .BeEquivalentTo( new Dictionary { - ["a"] = new Dictionary { ["1"] = "c" } + ["a"] = new Dictionary { ["1"] = "c" } } ); @@ -831,7 +831,7 @@ public void Decode_AllowsSpecifyingListIndices() .BeEquivalentTo( new Dictionary { - ["a"] = new Dictionary { ["0"] = "b", ["2"] = "c" } + ["a"] = new Dictionary { ["0"] = "b", ["2"] = "c" } } ); @@ -849,7 +849,7 @@ public void Decode_AllowsSpecifyingListIndices() .BeEquivalentTo( new Dictionary { - ["a"] = new Dictionary { ["1"] = "b", ["15"] = "c" } + ["a"] = new Dictionary { ["1"] = "b", ["15"] = "c" } } ); @@ -875,7 +875,7 @@ public void Decode_LimitsSpecificListIndices_ToListLimit() .BeEquivalentTo( new Dictionary { - ["a"] = new Dictionary { ["21"] = "a" } + ["a"] = new Dictionary { ["21"] = "a" } } ); @@ -888,7 +888,7 @@ public void Decode_LimitsSpecificListIndices_ToListLimit() .BeEquivalentTo( new Dictionary { - ["a"] = new Dictionary { ["21"] = "a" } + ["a"] = new Dictionary { ["21"] = "a" } } ); } @@ -964,7 +964,7 @@ public void Decode_TransformsListsToMaps() .BeEquivalentTo( new Dictionary { - ["foo"] = new Dictionary { ["0"] = "bar", ["bad"] = "baz" } + ["foo"] = new Dictionary { ["0"] = "bar", ["bad"] = "baz" } } ); @@ -973,7 +973,7 @@ public void Decode_TransformsListsToMaps() .BeEquivalentTo( new Dictionary { - ["foo"] = new Dictionary { ["bad"] = "baz", ["0"] = "bar" } + ["foo"] = new Dictionary { ["bad"] = "baz", ["0"] = "bar" } } ); @@ -982,7 +982,7 @@ public void Decode_TransformsListsToMaps() .BeEquivalentTo( new Dictionary { - ["foo"] = new Dictionary { ["bad"] = "baz", ["0"] = "bar" } + ["foo"] = new Dictionary { ["bad"] = "baz", ["0"] = "bar" } } ); @@ -991,7 +991,7 @@ public void Decode_TransformsListsToMaps() .BeEquivalentTo( new Dictionary { - ["foo"] = new Dictionary { ["0"] = "bar", ["bad"] = "baz" } + ["foo"] = new Dictionary { ["0"] = "bar", ["bad"] = "baz" } } ); @@ -1000,7 +1000,7 @@ public void Decode_TransformsListsToMaps() .BeEquivalentTo( new Dictionary { - ["foo"] = new Dictionary + ["foo"] = new Dictionary { ["bad"] = "baz", ["0"] = "bar", @@ -1108,7 +1108,7 @@ public void Decode_TransformsListsToMaps_DotNotation() .BeEquivalentTo( new Dictionary { - ["foo"] = new Dictionary { ["bad"] = "baz", ["0"] = "bar" } + ["foo"] = new Dictionary { ["bad"] = "baz", ["0"] = "bar" } } ); @@ -1117,7 +1117,7 @@ public void Decode_TransformsListsToMaps_DotNotation() .BeEquivalentTo( new Dictionary { - ["foo"] = new Dictionary { ["bad"] = "baz", ["0"] = "bar" } + ["foo"] = new Dictionary { ["bad"] = "baz", ["0"] = "bar" } } ); @@ -1126,7 +1126,7 @@ public void Decode_TransformsListsToMaps_DotNotation() .BeEquivalentTo( new Dictionary { - ["foo"] = new Dictionary { ["0"] = "bar", ["bad"] = "baz" } + ["foo"] = new Dictionary { ["0"] = "bar", ["bad"] = "baz" } } ); @@ -1135,7 +1135,7 @@ public void Decode_TransformsListsToMaps_DotNotation() .BeEquivalentTo( new Dictionary { - ["foo"] = new Dictionary + ["foo"] = new Dictionary { ["bad"] = "baz", ["0"] = "bar", @@ -1169,7 +1169,7 @@ public void Decode_CorrectlyPrunesUndefinedValues_WhenConvertingListToMap() .BeEquivalentTo( new Dictionary { - ["a"] = new Dictionary { ["2"] = "b", ["99999999"] = "c" } + ["a"] = new Dictionary { ["2"] = "b", ["99999999"] = "c" } } ); } @@ -1411,15 +1411,15 @@ public void Decode_ContinuesParsingWhenNoParentIsFound() { Qs.Decode("[]=&a=b") .Should() - .BeEquivalentTo(new Dictionary { ["0"] = "", ["a"] = "b" }); + .BeEquivalentTo(new Dictionary { ["0"] = "", ["a"] = "b" }); Qs.Decode("[]&a=b", new DecodeOptions { StrictNullHandling = true }) .Should() - .BeEquivalentTo(new Dictionary { ["0"] = null, ["a"] = "b" }); + .BeEquivalentTo(new Dictionary { ["0"] = null, ["a"] = "b" }); Qs.Decode("[foo]=bar") .Should() - .BeEquivalentTo(new Dictionary { ["foo"] = "bar" }); + .BeEquivalentTo(new Dictionary { ["foo"] = "bar" }); } [Fact] @@ -1441,7 +1441,7 @@ public void Decode_ParsesStringWithAlternativeStringDelimiter() { Qs.Decode("a=b;c=d", new DecodeOptions { Delimiter = new StringDelimiter(";") }) .Should() - .BeEquivalentTo(new Dictionary { ["a"] = "b", ["c"] = "d" }); + .BeEquivalentTo(new Dictionary { ["a"] = "b", ["c"] = "d" }); } [Fact] @@ -1449,7 +1449,7 @@ public void Decode_ParsesStringWithAlternativeRegexDelimiter() { Qs.Decode("a=b; c=d", new DecodeOptions { Delimiter = new RegexDelimiter(@"[;,] *") }) .Should() - .BeEquivalentTo(new Dictionary { ["a"] = "b", ["c"] = "d" }); + .BeEquivalentTo(new Dictionary { ["a"] = "b", ["c"] = "d" }); } [Fact] @@ -1457,7 +1457,7 @@ public void Decode_AllowsOverridingParameterLimit() { Qs.Decode("a=b&c=d", new DecodeOptions { ParameterLimit = 1 }) .Should() - .BeEquivalentTo(new Dictionary { ["a"] = "b" }); + .BeEquivalentTo(new Dictionary { ["a"] = "b" }); } [Fact] @@ -1465,7 +1465,7 @@ public void Decode_AllowsSettingParameterLimitToMaxValue() { Qs.Decode("a=b&c=d", new DecodeOptions { ParameterLimit = int.MaxValue }) .Should() - .BeEquivalentTo(new Dictionary { ["a"] = "b", ["c"] = "d" }); + .BeEquivalentTo(new Dictionary { ["a"] = "b", ["c"] = "d" }); } [Fact] @@ -1474,49 +1474,49 @@ public void Decode_AllowsOverridingListLimit() Qs.Decode("a[0]=b", new DecodeOptions { ListLimit = -1 }) .Should() .BeEquivalentTo( - new Dictionary + new Dictionary { - ["a"] = new Dictionary { ["0"] = "b" } + ["a"] = new Dictionary { ["0"] = "b" } } ); Qs.Decode("a[0]=b", new DecodeOptions { ListLimit = 0 }) .Should() - .BeEquivalentTo(new Dictionary { ["a"] = new List { "b" } }); + .BeEquivalentTo(new Dictionary { ["a"] = new List { "b" } }); Qs.Decode("a[-1]=b", new DecodeOptions { ListLimit = -1 }) .Should() .BeEquivalentTo( - new Dictionary + new Dictionary { - ["a"] = new Dictionary { ["-1"] = "b" } + ["a"] = new Dictionary { ["-1"] = "b" } } ); Qs.Decode("a[-1]=b", new DecodeOptions { ListLimit = 0 }) .Should() .BeEquivalentTo( - new Dictionary + new Dictionary { - ["a"] = new Dictionary { ["-1"] = "b" } + ["a"] = new Dictionary { ["-1"] = "b" } } ); Qs.Decode("a[0]=b&a[1]=c", new DecodeOptions { ListLimit = -1 }) .Should() .BeEquivalentTo( - new Dictionary + new Dictionary { - ["a"] = new Dictionary { ["0"] = "b", ["1"] = "c" } + ["a"] = new Dictionary { ["0"] = "b", ["1"] = "c" } } ); Qs.Decode("a[0]=b&a[1]=c", new DecodeOptions { ListLimit = 0 }) .Should() .BeEquivalentTo( - new Dictionary + new Dictionary { - ["a"] = new Dictionary { ["0"] = "b", ["1"] = "c" } + ["a"] = new Dictionary { ["0"] = "b", ["1"] = "c" } } ); } @@ -1527,18 +1527,18 @@ public void Decode_AllowsDisablingListParsing() Qs.Decode("a[0]=b&a[1]=c", new DecodeOptions { ParseLists = false }) .Should() .BeEquivalentTo( - new Dictionary + new Dictionary { - ["a"] = new Dictionary { ["0"] = "b", ["1"] = "c" } + ["a"] = new Dictionary { ["0"] = "b", ["1"] = "c" } } ); Qs.Decode("a[]=b", new DecodeOptions { ParseLists = false }) .Should() .BeEquivalentTo( - new Dictionary + new Dictionary { - ["a"] = new Dictionary { ["0"] = "b" } + ["a"] = new Dictionary { ["0"] = "b" } } ); } @@ -1548,15 +1548,15 @@ public void Decode_AllowsForQueryStringPrefix() { Qs.Decode("?foo=bar", new DecodeOptions { IgnoreQueryPrefix = true }) .Should() - .BeEquivalentTo(new Dictionary { ["foo"] = "bar" }); + .BeEquivalentTo(new Dictionary { ["foo"] = "bar" }); Qs.Decode("foo=bar", new DecodeOptions { IgnoreQueryPrefix = true }) .Should() - .BeEquivalentTo(new Dictionary { ["foo"] = "bar" }); + .BeEquivalentTo(new Dictionary { ["foo"] = "bar" }); Qs.Decode("?foo=bar", new DecodeOptions { IgnoreQueryPrefix = false }) .Should() - .BeEquivalentTo(new Dictionary { ["?foo"] = "bar" }); + .BeEquivalentTo(new Dictionary { ["?foo"] = "bar" }); } [Fact] @@ -1568,11 +1568,11 @@ public void Decode_ParsesMap() ["user[email]"] = null }; - var expected = new Dictionary + var expected = new Dictionary { - ["user"] = new Dictionary + ["user"] = new Dictionary { - ["name"] = new Dictionary { ["pop[bob]"] = 3 }, + ["name"] = new Dictionary { ["pop[bob]"] = 3 }, ["email"] = null } }; @@ -1586,7 +1586,7 @@ public void Decode_ParsesStringWithCommaAsListDivider() Qs.Decode("foo=bar,tee", new DecodeOptions { Comma = true }) .Should() .BeEquivalentTo( - new Dictionary + new Dictionary { ["foo"] = new List { "bar", "tee" } } @@ -1595,9 +1595,9 @@ public void Decode_ParsesStringWithCommaAsListDivider() Qs.Decode("foo[bar]=coffee,tee", new DecodeOptions { Comma = true }) .Should() .BeEquivalentTo( - new Dictionary + new Dictionary { - ["foo"] = new Dictionary + ["foo"] = new Dictionary { ["bar"] = new List { "coffee", "tee" } } @@ -1606,32 +1606,32 @@ public void Decode_ParsesStringWithCommaAsListDivider() Qs.Decode("foo=", new DecodeOptions { Comma = true }) .Should() - .BeEquivalentTo(new Dictionary { ["foo"] = "" }); + .BeEquivalentTo(new Dictionary { ["foo"] = "" }); Qs.Decode("foo", new DecodeOptions { Comma = true }) .Should() - .BeEquivalentTo(new Dictionary { ["foo"] = "" }); + .BeEquivalentTo(new Dictionary { ["foo"] = "" }); Qs.Decode("foo", new DecodeOptions { Comma = true, StrictNullHandling = true }) .Should() - .BeEquivalentTo(new Dictionary { ["foo"] = null }); + .BeEquivalentTo(new Dictionary { ["foo"] = null }); Qs.Decode("a[0]=c") .Should() - .BeEquivalentTo(new Dictionary { ["a"] = new List { "c" } }); + .BeEquivalentTo(new Dictionary { ["a"] = new List { "c" } }); Qs.Decode("a[]=c") .Should() - .BeEquivalentTo(new Dictionary { ["a"] = new List { "c" } }); + .BeEquivalentTo(new Dictionary { ["a"] = new List { "c" } }); Qs.Decode("a[]=c", new DecodeOptions { Comma = true }) .Should() - .BeEquivalentTo(new Dictionary { ["a"] = new List { "c" } }); + .BeEquivalentTo(new Dictionary { ["a"] = new List { "c" } }); Qs.Decode("a[0]=c&a[1]=d") .Should() .BeEquivalentTo( - new Dictionary + new Dictionary { ["a"] = new List { "c", "d" } } @@ -1640,7 +1640,7 @@ public void Decode_ParsesStringWithCommaAsListDivider() Qs.Decode("a[]=c&a[]=d") .Should() .BeEquivalentTo( - new Dictionary + new Dictionary { ["a"] = new List { "c", "d" } } @@ -1649,7 +1649,7 @@ public void Decode_ParsesStringWithCommaAsListDivider() Qs.Decode("a=c,d", new DecodeOptions { Comma = true }) .Should() .BeEquivalentTo( - new Dictionary + new Dictionary { ["a"] = new List { "c", "d" } } @@ -1662,13 +1662,13 @@ public void Decode_ParsesValuesWithCommaAsListDivider() var input1 = new Dictionary { ["foo"] = "bar,tee" }; Qs.Decode(input1, new DecodeOptions { Comma = false }) .Should() - .BeEquivalentTo(new Dictionary { ["foo"] = "bar,tee" }); + .BeEquivalentTo(new Dictionary { ["foo"] = "bar,tee" }); var input2 = new Dictionary { ["foo"] = "bar,tee" }; Qs.Decode(input2, new DecodeOptions { Comma = true }) .Should() .BeEquivalentTo( - new Dictionary + new Dictionary { ["foo"] = new List { "bar", "tee" } } @@ -1690,11 +1690,11 @@ public void Decode_UseNumberDecoderParsesStringThatHasOneNumberWithCommaOptionEn // For now, testing with default decoder Qs.Decode("foo=1", new DecodeOptions { Comma = true }) .Should() - .BeEquivalentTo(new Dictionary { ["foo"] = "1" }); + .BeEquivalentTo(new Dictionary { ["foo"] = "1" }); Qs.Decode("foo=0", new DecodeOptions { Comma = true }) .Should() - .BeEquivalentTo(new Dictionary { ["foo"] = "0" }); + .BeEquivalentTo(new Dictionary { ["foo"] = "0" }); } [Fact] @@ -1703,7 +1703,7 @@ public void Decode_ParsesBracketsHoldsListOfListsWhenHavingTwoPartsOfStringsWith Qs.Decode("foo[]=1,2,3&foo[]=4,5,6", new DecodeOptions { Comma = true }) .Should() .BeEquivalentTo( - new Dictionary + new Dictionary { ["foo"] = new List { @@ -1716,7 +1716,7 @@ public void Decode_ParsesBracketsHoldsListOfListsWhenHavingTwoPartsOfStringsWith Qs.Decode("foo[]=1,2,3&foo[]=", new DecodeOptions { Comma = true }) .Should() .BeEquivalentTo( - new Dictionary + new Dictionary { ["foo"] = new List { @@ -1729,7 +1729,7 @@ public void Decode_ParsesBracketsHoldsListOfListsWhenHavingTwoPartsOfStringsWith Qs.Decode("foo[]=1,2,3&foo[]=,", new DecodeOptions { Comma = true }) .Should() .BeEquivalentTo( - new Dictionary + new Dictionary { ["foo"] = new List { @@ -1742,7 +1742,7 @@ public void Decode_ParsesBracketsHoldsListOfListsWhenHavingTwoPartsOfStringsWith Qs.Decode("foo[]=1,2,3&foo[]=a", new DecodeOptions { Comma = true }) .Should() .BeEquivalentTo( - new Dictionary + new Dictionary { ["foo"] = new List { @@ -1758,12 +1758,12 @@ public void Decode_ParsesCommaDelimitedListWhileHavingPercentEncodedCommaTreated { Qs.Decode("foo=a%2Cb", new DecodeOptions { Comma = true }) .Should() - .BeEquivalentTo(new Dictionary { ["foo"] = "a,b" }); + .BeEquivalentTo(new Dictionary { ["foo"] = "a,b" }); Qs.Decode("foo=a%2C%20b,d", new DecodeOptions { Comma = true }) .Should() .BeEquivalentTo( - new Dictionary + new Dictionary { ["foo"] = new List { "a, b", "d" } } @@ -1772,7 +1772,7 @@ public void Decode_ParsesCommaDelimitedListWhileHavingPercentEncodedCommaTreated Qs.Decode("foo=a%2C%20b,c%2C%20d", new DecodeOptions { Comma = true }) .Should() .BeEquivalentTo( - new Dictionary + new Dictionary { ["foo"] = new List { "a, b", "c, d" } } @@ -1788,11 +1788,11 @@ public void Decode_ParsesMapInDotNotation() ["user.email."] = null }; - var expected = new Dictionary + var expected = new Dictionary { - ["user"] = new Dictionary + ["user"] = new Dictionary { - ["name"] = new Dictionary { ["pop[bob]"] = 3 }, + ["name"] = new Dictionary { ["pop[bob]"] = 3 }, ["email"] = null } }; @@ -1812,13 +1812,13 @@ public void Decode_ParsesMapAndNotChildValues() ["user[email]"] = null }; - var expected = new Dictionary + var expected = new Dictionary { - ["user"] = new Dictionary + ["user"] = new Dictionary { - ["name"] = new Dictionary + ["name"] = new Dictionary { - ["pop[bob]"] = new Dictionary { ["test"] = 3 } + ["pop[bob]"] = new Dictionary { ["test"] = 3 } }, ["email"] = null } @@ -1834,7 +1834,7 @@ public void Decode_DoesNotCrashWhenParsingCircularReferences() var a = new Dictionary(); a["b"] = a; - Dictionary parsed = null!; + Dictionary parsed = null!; var action = () => { @@ -1846,7 +1846,7 @@ public void Decode_DoesNotCrashWhenParsingCircularReferences() parsed.Should().ContainKey("foo"); - var fooValue = parsed["foo"].Should().BeOfType>().Subject; + var fooValue = parsed["foo"].Should().BeOfType>().Subject; fooValue.Should().ContainKey("bar"); fooValue.Should().ContainKey("baz"); fooValue["bar"].Should().Be("baz"); @@ -1864,7 +1864,7 @@ public void Decode_DoesNotCrashOrTimeOutWhenParsingDeepMaps() str.Append("=bar"); - Dictionary parsed = null!; + Dictionary parsed = null!; var action = void () => parsed = Qs.Decode(str.ToString(), new DecodeOptions { Depth = depth }); @@ -1875,7 +1875,7 @@ public void Decode_DoesNotCrashOrTimeOutWhenParsingDeepMaps() var actualDepth = 0; var reference = parsed["foo"]; - while (reference is Dictionary dict && dict.ContainsKey("p")) + while (reference is Dictionary dict && dict.ContainsKey("p")) { reference = dict["p"]; actualDepth++; @@ -1888,10 +1888,10 @@ public void Decode_DoesNotCrashOrTimeOutWhenParsingDeepMaps() public void Decode_ParsesNullMapsCorrectly() { var a = new Dictionary { ["b"] = "c" }; - Qs.Decode(a).Should().BeEquivalentTo(new Dictionary { ["b"] = "c" }); + Qs.Decode(a).Should().BeEquivalentTo(new Dictionary { ["b"] = "c" }); Qs.Decode(new Dictionary { ["a"] = a }) .Should() - .BeEquivalentTo(new Dictionary { ["a"] = a }); + .BeEquivalentTo(new Dictionary { ["a"] = a }); } [Fact] @@ -1900,7 +1900,7 @@ public void Decode_ParsesDatesCorrectly() var now = DateTime.Now; Qs.Decode(new Dictionary { ["a"] = now }) .Should() - .BeEquivalentTo(new Dictionary { ["a"] = now }); + .BeEquivalentTo(new Dictionary { ["a"] = now }); } [Fact] @@ -1909,7 +1909,7 @@ public void Decode_ParsesRegularExpressionsCorrectly() var re = new Regex("^test$"); Qs.Decode(new Dictionary { ["a"] = re }) .Should() - .BeEquivalentTo(new Dictionary { ["a"] = re }); + .BeEquivalentTo(new Dictionary { ["a"] = re }); } [Fact] @@ -1917,13 +1917,13 @@ public void Decode_ParamsStartingWithClosingBracket() { Qs.Decode("]=toString") .Should() - .BeEquivalentTo(new Dictionary { ["]"] = "toString" }); + .BeEquivalentTo(new Dictionary { ["]"] = "toString" }); Qs.Decode("]]=toString") .Should() - .BeEquivalentTo(new Dictionary { ["]]"] = "toString" }); + .BeEquivalentTo(new Dictionary { ["]]"] = "toString" }); Qs.Decode("]hello]=toString") .Should() - .BeEquivalentTo(new Dictionary { ["]hello]"] = "toString" }); + .BeEquivalentTo(new Dictionary { ["]hello]"] = "toString" }); } [Fact] @@ -1931,13 +1931,13 @@ public void Decode_ParamsStartingWithStartingBracket() { Qs.Decode("[=toString") .Should() - .BeEquivalentTo(new Dictionary { ["["] = "toString" }); + .BeEquivalentTo(new Dictionary { ["["] = "toString" }); Qs.Decode("[[=toString") .Should() - .BeEquivalentTo(new Dictionary { ["[["] = "toString" }); + .BeEquivalentTo(new Dictionary { ["[["] = "toString" }); Qs.Decode("[hello[=toString") .Should() - .BeEquivalentTo(new Dictionary { ["[hello["] = "toString" }); + .BeEquivalentTo(new Dictionary { ["[hello["] = "toString" }); } [Fact] @@ -1946,9 +1946,9 @@ public void Decode_AddKeysToMaps() Qs.Decode("a[b]=c") .Should() .BeEquivalentTo( - new Dictionary + new Dictionary { - ["a"] = new Dictionary { ["b"] = "c" } + ["a"] = new Dictionary { ["b"] = "c" } } ); } @@ -1956,23 +1956,23 @@ public void Decode_AddKeysToMaps() [Fact] public void Decode_CanReturnNullMaps() { - var expected = new Dictionary + var expected = new Dictionary { - ["a"] = new Dictionary() + ["a"] = new Dictionary() }; - ((Dictionary)expected["a"]!)["b"] = "c"; - ((Dictionary)expected["a"]!)["hasOwnProperty"] = "d"; + ((Dictionary)expected["a"]!)["b"] = "c"; + ((Dictionary)expected["a"]!)["hasOwnProperty"] = "d"; Qs.Decode("a[b]=c&a[hasOwnProperty]=d").Should().BeEquivalentTo(expected); - Qs.Decode(null).Should().BeEquivalentTo(new Dictionary()); + Qs.Decode(null).Should().BeEquivalentTo(new Dictionary()); - var expectedList = new Dictionary + var expectedList = new Dictionary { - ["a"] = new Dictionary() + ["a"] = new Dictionary() }; - ((Dictionary)expectedList["a"]!)["0"] = "b"; - ((Dictionary)expectedList["a"]!)["c"] = "d"; + ((Dictionary)expectedList["a"]!)["0"] = "b"; + ((Dictionary)expectedList["a"]!)["c"] = "d"; Qs.Decode("a[]=b&a[c]=d").Should().BeEquivalentTo(expectedList); } @@ -1980,7 +1980,7 @@ public void Decode_CanReturnNullMaps() [Fact] public void Decode_CanParseWithCustomEncoding() { - var expected = new Dictionary { ["県"] = "大阪府" }; + var expected = new Dictionary { ["県"] = "大阪府" }; string? CustomDecoder(string? str, Encoding? charset) { @@ -1994,7 +1994,7 @@ public void Decode_CanParseWithCustomEncoding() [Fact] public void Decode_ParsesIso88591StringIfAsked() { - var expected = new Dictionary { ["¢"] = "½" }; + var expected = new Dictionary { ["¢"] = "½" }; var options = new DecodeOptions { Charset = Encoding.Latin1 }; Qs.Decode("%A2=%BD", options).Should().BeEquivalentTo(expected); @@ -2025,7 +2025,7 @@ public void Decode_PrefersUtf8CharsetSpecifiedByUtf8SentinelToDefaultCharsetOfIs options ) .Should() - .BeEquivalentTo(new Dictionary { ["ø"] = "ø" }); + .BeEquivalentTo(new Dictionary { ["ø"] = "ø" }); } [Fact] @@ -2041,7 +2041,7 @@ public void Decode_PrefersIso88591CharsetSpecifiedByUtf8SentinelToDefaultCharset options ) .Should() - .BeEquivalentTo(new Dictionary { ["ø"] = "ø" }); + .BeEquivalentTo(new Dictionary { ["ø"] = "ø" }); } [Fact] @@ -2054,7 +2054,7 @@ public void Decode_DoesNotRequireUtf8SentinelToBeDefinedBeforeParametersWhoseDec Qs.Decode($"a={urlEncodedOSlashInUtf8}&utf8={urlEncodedNumCheckmark}", options) .Should() - .BeEquivalentTo(new Dictionary { ["a"] = "ø" }); + .BeEquivalentTo(new Dictionary { ["a"] = "ø" }); } [Fact] @@ -2066,7 +2066,7 @@ public void Decode_ShouldIgnoreUtf8SentinelWithUnknownValue() Qs.Decode($"utf8=foo&{urlEncodedOSlashInUtf8}={urlEncodedOSlashInUtf8}", options) .Should() - .BeEquivalentTo(new Dictionary { ["ø"] = "ø" }); + .BeEquivalentTo(new Dictionary { ["ø"] = "ø" }); } [Fact] @@ -2082,7 +2082,7 @@ public void Decode_UsesUtf8SentinelToSwitchToUtf8WhenNoDefaultCharsetIsGiven() options ) .Should() - .BeEquivalentTo(new Dictionary { ["ø"] = "ø" }); + .BeEquivalentTo(new Dictionary { ["ø"] = "ø" }); } [Fact] @@ -2098,7 +2098,7 @@ public void Decode_UsesUtf8SentinelToSwitchToIso88591WhenNoDefaultCharsetIsGiven options ) .Should() - .BeEquivalentTo(new Dictionary { ["ø"] = "ø" }); + .BeEquivalentTo(new Dictionary { ["ø"] = "ø" }); } [Fact] @@ -2114,7 +2114,7 @@ public void Decode_InterpretsNumericEntitiesInIso88591WhenInterpretNumericEntiti Qs.Decode($"foo={urlEncodedNumSmiley}", options) .Should() - .BeEquivalentTo(new Dictionary { ["foo"] = "☺" }); + .BeEquivalentTo(new Dictionary { ["foo"] = "☺" }); } [Fact] @@ -2136,7 +2136,7 @@ public void Decode_HandlesCustomDecoderReturningNullInIso88591CharsetWhenInterpr Qs.Decode($"foo=&bar={urlEncodedNumSmiley}", options) .Should() - .BeEquivalentTo(new Dictionary { ["foo"] = null, ["bar"] = "☺" }); + .BeEquivalentTo(new Dictionary { ["foo"] = null, ["bar"] = "☺" }); } [Fact] @@ -2148,7 +2148,7 @@ public void Decode_DoesNotInterpretNumericEntitiesInIso88591WhenInterpretNumeric Qs.Decode($"foo={urlEncodedNumSmiley}", options) .Should() - .BeEquivalentTo(new Dictionary { ["foo"] = "☺" }); + .BeEquivalentTo(new Dictionary { ["foo"] = "☺" }); } [Fact] @@ -2166,7 +2166,7 @@ public void Decode_InterpretNumericEntitiesWithCommaAndIso88591CharsetDoesNotCra Qs.Decode($"b&a[]=1,{urlEncodedNumSmiley}", options) .Should() .BeEquivalentTo( - new Dictionary + new Dictionary { ["b"] = "", ["a"] = new List { "1,☺" } @@ -2187,7 +2187,7 @@ public void Decode_DoesNotInterpretNumericEntitiesWhenCharsetIsUtf8EvenWhenInter Qs.Decode($"foo={urlEncodedNumSmiley}", options) .Should() - .BeEquivalentTo(new Dictionary { ["foo"] = "☺" }); + .BeEquivalentTo(new Dictionary { ["foo"] = "☺" }); } [Fact] @@ -2197,14 +2197,14 @@ public void Decode_DoesNotInterpretUXXXXSyntaxInIso88591Mode() Qs.Decode("%u263A=%u263A", options) .Should() - .BeEquivalentTo(new Dictionary { ["%u263A"] = "%u263A" }); + .BeEquivalentTo(new Dictionary { ["%u263A"] = "%u263A" }); } [Theory] [MemberData(nameof(EmptyTestCases))] public void Decode_ParsesEmptyKeys_SkipsEmptyStringKey( string input, - Dictionary noEmptyKeys + Dictionary noEmptyKeys ) { Qs.Decode(input).Should().BeEquivalentTo(noEmptyKeys); @@ -2214,11 +2214,11 @@ public static IEnumerable EmptyTestCases() { // You'll need to define the actual test cases here based on your EmptyTestCases data // This is just an example structure - yield return new object[] { "=value", new Dictionary() }; + yield return new object[] { "=value", new Dictionary() }; yield return new object[] { "key=", - new Dictionary { ["key"] = "" } + new Dictionary { ["key"] = "" } }; // Add more test cases as needed } @@ -2229,7 +2229,7 @@ public void Decode_Duplicates_Default_Combine() Qs.Decode("foo=bar&foo=baz") .Should() .BeEquivalentTo( - new Dictionary + new Dictionary { ["foo"] = new List { "bar", "baz" } } @@ -2243,7 +2243,7 @@ public void Decode_Duplicates_Combine() Qs.Decode("foo=bar&foo=baz", options) .Should() .BeEquivalentTo( - new Dictionary + new Dictionary { ["foo"] = new List { "bar", "baz" } } @@ -2256,7 +2256,7 @@ public void Decode_Duplicates_First() var options = new DecodeOptions { Duplicates = Duplicates.First }; Qs.Decode("foo=bar&foo=baz", options) .Should() - .BeEquivalentTo(new Dictionary { ["foo"] = "bar" }); + .BeEquivalentTo(new Dictionary { ["foo"] = "bar" }); } [Fact] @@ -2265,7 +2265,7 @@ public void Decode_Duplicates_Last() var options = new DecodeOptions { Duplicates = Duplicates.Last }; Qs.Decode("foo=bar&foo=baz", options) .Should() - .BeEquivalentTo(new Dictionary { ["foo"] = "baz" }); + .BeEquivalentTo(new Dictionary { ["foo"] = "baz" }); } [Fact] @@ -2321,9 +2321,9 @@ public void Decode_StrictDepth_ParsesSuccessfullyWhenDepthIsWithinLimitWithStric Qs.Decode("a[b]=c", options) .Should() .BeEquivalentTo( - new Dictionary + new Dictionary { - ["a"] = new Dictionary { ["b"] = "c" } + ["a"] = new Dictionary { ["b"] = "c" } } ); } @@ -2336,11 +2336,11 @@ public void Decode_StrictDepth_DoesNotThrowWhenDepthExceedsLimitWithStrictDepthF Qs.Decode("a[b][c][d][e][f][g][h][i]=j", options) .Should() .BeEquivalentTo( - new Dictionary + new Dictionary { - ["a"] = new Dictionary + ["a"] = new Dictionary { - ["b"] = new Dictionary { ["[c][d][e][f][g][h][i]"] = "j" } + ["b"] = new Dictionary { ["[c][d][e][f][g][h][i]"] = "j" } } } ); @@ -2354,9 +2354,9 @@ public void Decode_StrictDepth_ParsesSuccessfullyWhenDepthIsWithinLimitWithStric Qs.Decode("a[b]=c", options) .Should() .BeEquivalentTo( - new Dictionary + new Dictionary { - ["a"] = new Dictionary { ["b"] = "c" } + ["a"] = new Dictionary { ["b"] = "c" } } ); } @@ -2369,11 +2369,11 @@ public void Decode_StrictDepth_DoesNotThrowWhenDepthIsExactlyAtLimitWithStrictDe Qs.Decode("a[b][c]=d", options) .Should() .BeEquivalentTo( - new Dictionary + new Dictionary { - ["a"] = new Dictionary + ["a"] = new Dictionary { - ["b"] = new Dictionary { ["c"] = "d" } + ["b"] = new Dictionary { ["c"] = "d" } } } ); @@ -2387,7 +2387,7 @@ public void Decode_ParameterLimit_DoesNotThrowErrorWhenWithinParameterLimit() Qs.Decode("a=1&b=2&c=3", options) .Should() .BeEquivalentTo( - new Dictionary + new Dictionary { ["a"] = "1", ["b"] = "2", @@ -2413,7 +2413,7 @@ public void Decode_ParameterLimit_SilentlyTruncatesWhenThrowOnLimitExceededIsNot Qs.Decode("a=1&b=2&c=3&d=4&e=5", options) .Should() .BeEquivalentTo( - new Dictionary + new Dictionary { ["a"] = "1", ["b"] = "2", @@ -2430,7 +2430,7 @@ public void Decode_ParameterLimit_SilentlyTruncatesWhenParameterLimitExceededWit Qs.Decode("a=1&b=2&c=3&d=4&e=5", options) .Should() .BeEquivalentTo( - new Dictionary + new Dictionary { ["a"] = "1", ["b"] = "2", @@ -2447,7 +2447,7 @@ public void Decode_ParameterLimit_AllowsUnlimitedParametersWhenParameterLimitSet Qs.Decode("a=1&b=2&c=3&d=4&e=5&f=6", options) .Should() .BeEquivalentTo( - new Dictionary + new Dictionary { ["a"] = "1", ["b"] = "2", @@ -2467,7 +2467,7 @@ public void Decode_ListLimit_DoesNotThrowErrorWhenListIsWithinLimit() Qs.Decode("a[]=1&a[]=2&a[]=3", options) .Should() .BeEquivalentTo( - new Dictionary + new Dictionary { ["a"] = new List { "1", "2", "3" } } @@ -2491,9 +2491,9 @@ public void Decode_ListLimit_ConvertsListToMapIfLengthIsGreaterThanLimit() Qs.Decode("a[1]=1&a[2]=2&a[3]=3&a[4]=4&a[5]=5&a[6]=6", options) .Should() .BeEquivalentTo( - new Dictionary + new Dictionary { - ["a"] = new Dictionary + ["a"] = new Dictionary { ["1"] = "1", ["2"] = "2", @@ -2514,7 +2514,7 @@ public void Decode_ListLimit_HandlesListLimitOfZeroCorrectly() Qs.Decode("a[]=1&a[]=2", options) .Should() .BeEquivalentTo( - new Dictionary + new Dictionary { ["a"] = new List { "1", "2" } } @@ -2547,83 +2547,83 @@ public void ShouldParseASimpleString() Qs.Decode("0=foo", options) .Should() - .BeEquivalentTo(new Dictionary { ["0"] = "foo" }); + .BeEquivalentTo(new Dictionary { ["0"] = "foo" }); Qs.Decode("foo=c++", options) .Should() - .BeEquivalentTo(new Dictionary { ["foo"] = "c " }); + .BeEquivalentTo(new Dictionary { ["foo"] = "c " }); Qs.Decode("a[>=]=23", options) .Should() .BeEquivalentTo( - new Dictionary + new Dictionary { - ["a"] = new Dictionary { [">="] = "23" } + ["a"] = new Dictionary { [">="] = "23" } } ); Qs.Decode("a[<=>]==23", options) .Should() .BeEquivalentTo( - new Dictionary + new Dictionary { - ["a"] = new Dictionary { ["<=>"] = "=23" } + ["a"] = new Dictionary { ["<=>"] = "=23" } } ); Qs.Decode("a[==]=23", options) .Should() .BeEquivalentTo( - new Dictionary + new Dictionary { - ["a"] = new Dictionary { ["=="] = "23" } + ["a"] = new Dictionary { ["=="] = "23" } } ); Qs.Decode("foo", optionsStrictNullHandling) .Should() - .BeEquivalentTo(new Dictionary { ["foo"] = null }); + .BeEquivalentTo(new Dictionary { ["foo"] = null }); Qs.Decode("foo", options) .Should() - .BeEquivalentTo(new Dictionary { ["foo"] = "" }); + .BeEquivalentTo(new Dictionary { ["foo"] = "" }); Qs.Decode("foo=", options) .Should() - .BeEquivalentTo(new Dictionary { ["foo"] = "" }); + .BeEquivalentTo(new Dictionary { ["foo"] = "" }); Qs.Decode("foo=bar", options) .Should() - .BeEquivalentTo(new Dictionary { ["foo"] = "bar" }); + .BeEquivalentTo(new Dictionary { ["foo"] = "bar" }); Qs.Decode(" foo = bar = baz ", options) .Should() - .BeEquivalentTo(new Dictionary { [" foo "] = " bar = baz " }); + .BeEquivalentTo(new Dictionary { [" foo "] = " bar = baz " }); Qs.Decode("foo=bar=baz", options) .Should() - .BeEquivalentTo(new Dictionary { ["foo"] = "bar=baz" }); + .BeEquivalentTo(new Dictionary { ["foo"] = "bar=baz" }); Qs.Decode("foo=bar&bar=baz", options) .Should() - .BeEquivalentTo(new Dictionary { ["foo"] = "bar", ["bar"] = "baz" }); + .BeEquivalentTo(new Dictionary { ["foo"] = "bar", ["bar"] = "baz" }); Qs.Decode("foo2=bar2&baz2=", options) .Should() - .BeEquivalentTo(new Dictionary { ["foo2"] = "bar2", ["baz2"] = "" }); + .BeEquivalentTo(new Dictionary { ["foo2"] = "bar2", ["baz2"] = "" }); Qs.Decode("foo=bar&baz", optionsStrictNullHandling) .Should() - .BeEquivalentTo(new Dictionary { ["foo"] = "bar", ["baz"] = null }); + .BeEquivalentTo(new Dictionary { ["foo"] = "bar", ["baz"] = null }); Qs.Decode("foo=bar&baz", options) .Should() - .BeEquivalentTo(new Dictionary { ["foo"] = "bar", ["baz"] = "" }); + .BeEquivalentTo(new Dictionary { ["foo"] = "bar", ["baz"] = "" }); Qs.Decode("cht=p3&chd=t:60,40&chs=250x100&chl=Hello|World", options) .Should() .BeEquivalentTo( - new Dictionary + new Dictionary { ["cht"] = "p3", ["chd"] = "t:60,40", @@ -2641,7 +2641,7 @@ public void ShouldHandleArraysOnTheSameKey() Qs.Decode("a[]=b&a[]=c", options) .Should() .BeEquivalentTo( - new Dictionary + new Dictionary { ["a"] = new List { "b", "c" } } @@ -2650,7 +2650,7 @@ public void ShouldHandleArraysOnTheSameKey() Qs.Decode("a[0]=b&a[1]=c", options) .Should() .BeEquivalentTo( - new Dictionary + new Dictionary { ["a"] = new List { "b", "c" } } @@ -2658,12 +2658,12 @@ public void ShouldHandleArraysOnTheSameKey() Qs.Decode("a=b,c", options) .Should() - .BeEquivalentTo(new Dictionary { ["a"] = "b,c" }); + .BeEquivalentTo(new Dictionary { ["a"] = "b,c" }); Qs.Decode("a=b&a=c", options) .Should() .BeEquivalentTo( - new Dictionary + new Dictionary { ["a"] = new List { "b", "c" } } @@ -2678,14 +2678,14 @@ public void ShouldAllowDotNotation() Qs.Decode("a.b=c", options) .Should() - .BeEquivalentTo(new Dictionary { ["a.b"] = "c" }); + .BeEquivalentTo(new Dictionary { ["a.b"] = "c" }); Qs.Decode("a.b=c", optionsAllowDots) .Should() .BeEquivalentTo( - new Dictionary + new Dictionary { - ["a"] = new Dictionary { ["b"] = "c" } + ["a"] = new Dictionary { ["b"] = "c" } } ); } @@ -2700,20 +2700,20 @@ public void ShouldHandleDepthParsing() Qs.Decode("a[b]=c", options) .Should() .BeEquivalentTo( - new Dictionary + new Dictionary { - ["a"] = new Dictionary { ["b"] = "c" } + ["a"] = new Dictionary { ["b"] = "c" } } ); Qs.Decode("a[b][c]=d", options) .Should() .BeEquivalentTo( - new Dictionary + new Dictionary { - ["a"] = new Dictionary + ["a"] = new Dictionary { - ["b"] = new Dictionary { ["c"] = "d" } + ["b"] = new Dictionary { ["c"] = "d" } } } ); @@ -2721,19 +2721,19 @@ public void ShouldHandleDepthParsing() Qs.Decode("a[b][c][d][e][f][g][h]=i", options) .Should() .BeEquivalentTo( - new Dictionary + new Dictionary { - ["a"] = new Dictionary + ["a"] = new Dictionary { - ["b"] = new Dictionary + ["b"] = new Dictionary { - ["c"] = new Dictionary + ["c"] = new Dictionary { - ["d"] = new Dictionary + ["d"] = new Dictionary { - ["e"] = new Dictionary + ["e"] = new Dictionary { - ["f"] = new Dictionary + ["f"] = new Dictionary { ["[g][h]"] = "i" } @@ -2748,11 +2748,11 @@ public void ShouldHandleDepthParsing() Qs.Decode("a[b][c]=d", optionsDepth1) .Should() .BeEquivalentTo( - new Dictionary + new Dictionary { - ["a"] = new Dictionary + ["a"] = new Dictionary { - ["b"] = new Dictionary { ["[c]"] = "d" } + ["b"] = new Dictionary { ["[c]"] = "d" } } } ); @@ -2760,23 +2760,23 @@ public void ShouldHandleDepthParsing() Qs.Decode("a[b][c][d]=e", optionsDepth1) .Should() .BeEquivalentTo( - new Dictionary + new Dictionary { - ["a"] = new Dictionary + ["a"] = new Dictionary { - ["b"] = new Dictionary { ["[c][d]"] = "e" } + ["b"] = new Dictionary { ["[c][d]"] = "e" } } } ); Qs.Decode("a[0]=b&a[1]=c", optionsDepth0) .Should() - .BeEquivalentTo(new Dictionary { ["a[0]"] = "b", ["a[1]"] = "c" }); + .BeEquivalentTo(new Dictionary { ["a[0]"] = "b", ["a[1]"] = "c" }); Qs.Decode("a[0][0]=b&a[0][1]=c&a[1]=d&e=2", optionsDepth0) .Should() .BeEquivalentTo( - new Dictionary + new Dictionary { ["a[0][0]"] = "b", ["a[0][1]"] = "c", @@ -2793,12 +2793,12 @@ public void ShouldParseAnExplicitArray() Qs.Decode("a[]=b", options) .Should() - .BeEquivalentTo(new Dictionary { ["a"] = new List { "b" } }); + .BeEquivalentTo(new Dictionary { ["a"] = new List { "b" } }); Qs.Decode("a[]=b&a[]=c", options) .Should() .BeEquivalentTo( - new Dictionary + new Dictionary { ["a"] = new List { "b", "c" } } @@ -2807,7 +2807,7 @@ public void ShouldParseAnExplicitArray() Qs.Decode("a[]=b&a[]=c&a[]=d", options) .Should() .BeEquivalentTo( - new Dictionary + new Dictionary { ["a"] = new List { "b", "c", "d" } } @@ -2824,7 +2824,7 @@ public void ShouldParseAMixOfSimpleAndExplicitArrays() Qs.Decode("a=b&a[]=c", options) .Should() .BeEquivalentTo( - new Dictionary + new Dictionary { ["a"] = new List { "b", "c" } } @@ -2833,7 +2833,7 @@ public void ShouldParseAMixOfSimpleAndExplicitArrays() Qs.Decode("a[]=b&a=c", options) .Should() .BeEquivalentTo( - new Dictionary + new Dictionary { ["a"] = new List { "b", "c" } } @@ -2842,7 +2842,7 @@ public void ShouldParseAMixOfSimpleAndExplicitArrays() Qs.Decode("a[0]=b&a=c", options) .Should() .BeEquivalentTo( - new Dictionary + new Dictionary { ["a"] = new List { "b", "c" } } @@ -2851,7 +2851,7 @@ public void ShouldParseAMixOfSimpleAndExplicitArrays() Qs.Decode("a=b&a[0]=c", options) .Should() .BeEquivalentTo( - new Dictionary + new Dictionary { ["a"] = new List { "b", "c" } } @@ -2860,7 +2860,7 @@ public void ShouldParseAMixOfSimpleAndExplicitArrays() Qs.Decode("a[1]=b&a=c", options20) .Should() .BeEquivalentTo( - new Dictionary + new Dictionary { ["a"] = new List { "b", "c" } } @@ -2869,7 +2869,7 @@ public void ShouldParseAMixOfSimpleAndExplicitArrays() Qs.Decode("a[]=b&a=c", options0) .Should() .BeEquivalentTo( - new Dictionary + new Dictionary { ["a"] = new List { "b", "c" } } @@ -2878,7 +2878,7 @@ public void ShouldParseAMixOfSimpleAndExplicitArrays() Qs.Decode("a=b&a[1]=c", options20) .Should() .BeEquivalentTo( - new Dictionary + new Dictionary { ["a"] = new List { "b", "c" } } @@ -2887,7 +2887,7 @@ public void ShouldParseAMixOfSimpleAndExplicitArrays() Qs.Decode("a=b&a[]=c", options0) .Should() .BeEquivalentTo( - new Dictionary + new Dictionary { ["a"] = new List { "b", "c" } } @@ -2902,9 +2902,9 @@ public void ShouldParseANestedArray() Qs.Decode("a[b][]=c&a[b][]=d", options) .Should() .BeEquivalentTo( - new Dictionary + new Dictionary { - ["a"] = new Dictionary + ["a"] = new Dictionary { ["b"] = new List { "c", "d" } } @@ -2914,9 +2914,9 @@ public void ShouldParseANestedArray() Qs.Decode("a[>=]=25", options) .Should() .BeEquivalentTo( - new Dictionary + new Dictionary { - ["a"] = new Dictionary { [">="] = "25" } + ["a"] = new Dictionary { [">="] = "25" } } ); } @@ -2931,7 +2931,7 @@ public void ShouldAllowSpecifyingArrayIndices() Qs.Decode("a[1]=c&a[0]=b&a[2]=d", options) .Should() .BeEquivalentTo( - new Dictionary + new Dictionary { ["a"] = new List { "b", "c", "d" } } @@ -2940,7 +2940,7 @@ public void ShouldAllowSpecifyingArrayIndices() Qs.Decode("a[1]=c&a[0]=b", options) .Should() .BeEquivalentTo( - new Dictionary + new Dictionary { ["a"] = new List { "b", "c" } } @@ -2948,20 +2948,20 @@ public void ShouldAllowSpecifyingArrayIndices() Qs.Decode("a[1]=c", options20) .Should() - .BeEquivalentTo(new Dictionary { ["a"] = new List { "c" } }); + .BeEquivalentTo(new Dictionary { ["a"] = new List { "c" } }); Qs.Decode("a[1]=c", options0) .Should() .BeEquivalentTo( - new Dictionary + new Dictionary { - ["a"] = new Dictionary { ["1"] = "c" } + ["a"] = new Dictionary { ["1"] = "c" } } ); Qs.Decode("a[1]=c", options) .Should() - .BeEquivalentTo(new Dictionary { ["a"] = new List { "c" } }); + .BeEquivalentTo(new Dictionary { ["a"] = new List { "c" } }); } [Fact] @@ -2972,27 +2972,27 @@ public void ShouldLimitSpecificArrayIndicesToListLimit() Qs.Decode("a[20]=a", options20) .Should() - .BeEquivalentTo(new Dictionary { ["a"] = new List { "a" } }); + .BeEquivalentTo(new Dictionary { ["a"] = new List { "a" } }); Qs.Decode("a[21]=a", options20) .Should() .BeEquivalentTo( - new Dictionary + new Dictionary { - ["a"] = new Dictionary { ["21"] = "a" } + ["a"] = new Dictionary { ["21"] = "a" } } ); Qs.Decode("a[20]=a", options) .Should() - .BeEquivalentTo(new Dictionary { ["a"] = new List { "a" } }); + .BeEquivalentTo(new Dictionary { ["a"] = new List { "a" } }); Qs.Decode("a[21]=a", options) .Should() .BeEquivalentTo( - new Dictionary + new Dictionary { - ["a"] = new Dictionary { ["21"] = "a" } + ["a"] = new Dictionary { ["21"] = "a" } } ); } @@ -3005,9 +3005,9 @@ public void ShouldSupportKeysThatBeginWithANumber() Qs.Decode("a[12b]=c", options) .Should() .BeEquivalentTo( - new Dictionary + new Dictionary { - ["a"] = new Dictionary { ["12b"] = "c" } + ["a"] = new Dictionary { ["12b"] = "c" } } ); } @@ -3019,7 +3019,7 @@ public void ShouldSupportEncodedEqualSigns() Qs.Decode("he%3Dllo=th%3Dere", options) .Should() - .BeEquivalentTo(new Dictionary { ["he=llo"] = "th=ere" }); + .BeEquivalentTo(new Dictionary { ["he=llo"] = "th=ere" }); } [Fact] @@ -3030,18 +3030,18 @@ public void ShouldHandleUrlEncodedStrings() Qs.Decode("a[b%20c]=d", options) .Should() .BeEquivalentTo( - new Dictionary + new Dictionary { - ["a"] = new Dictionary { ["b c"] = "d" } + ["a"] = new Dictionary { ["b c"] = "d" } } ); Qs.Decode("a[b]=c%20d", options) .Should() .BeEquivalentTo( - new Dictionary + new Dictionary { - ["a"] = new Dictionary { ["b"] = "c d" } + ["a"] = new Dictionary { ["b"] = "c d" } } ); } @@ -3053,11 +3053,11 @@ public void ShouldAllowBracketsInTheValue() Qs.Decode("pets=[\"tobi\"]", options) .Should() - .BeEquivalentTo(new Dictionary { ["pets"] = "[\"tobi\"]" }); + .BeEquivalentTo(new Dictionary { ["pets"] = "[\"tobi\"]" }); Qs.Decode("operators=[\">=\", \"<=\"]", options) .Should() - .BeEquivalentTo(new Dictionary { ["operators"] = "[\">=\", \"<=\"]" }); + .BeEquivalentTo(new Dictionary { ["operators"] = "[\">=\", \"<=\"]" }); } [Fact] @@ -3065,9 +3065,9 @@ public void ShouldAllowEmptyValues() { var options = new DecodeOptions(); - Qs.Decode("", options).Should().BeEquivalentTo(new Dictionary()); + Qs.Decode("", options).Should().BeEquivalentTo(new Dictionary()); - Qs.Decode(null, options).Should().BeEquivalentTo(new Dictionary()); + Qs.Decode(null, options).Should().BeEquivalentTo(new Dictionary()); } [Fact] @@ -3078,45 +3078,45 @@ public void ShouldTransformArraysToObjects() Qs.Decode("foo[0]=bar&foo[bad]=baz", options) .Should() .BeEquivalentTo( - new Dictionary + new Dictionary { - ["foo"] = new Dictionary { ["0"] = "bar", ["bad"] = "baz" } + ["foo"] = new Dictionary { ["0"] = "bar", ["bad"] = "baz" } } ); Qs.Decode("foo[bad]=baz&foo[0]=bar", options) .Should() .BeEquivalentTo( - new Dictionary + new Dictionary { - ["foo"] = new Dictionary { ["bad"] = "baz", ["0"] = "bar" } + ["foo"] = new Dictionary { ["bad"] = "baz", ["0"] = "bar" } } ); Qs.Decode("foo[bad]=baz&foo[]=bar", options) .Should() .BeEquivalentTo( - new Dictionary + new Dictionary { - ["foo"] = new Dictionary { ["bad"] = "baz", ["0"] = "bar" } + ["foo"] = new Dictionary { ["bad"] = "baz", ["0"] = "bar" } } ); Qs.Decode("foo[]=bar&foo[bad]=baz", options) .Should() .BeEquivalentTo( - new Dictionary + new Dictionary { - ["foo"] = new Dictionary { ["0"] = "bar", ["bad"] = "baz" } + ["foo"] = new Dictionary { ["0"] = "bar", ["bad"] = "baz" } } ); Qs.Decode("foo[bad]=baz&foo[]=bar&foo[]=foo", options) .Should() .BeEquivalentTo( - new Dictionary + new Dictionary { - ["foo"] = new Dictionary + ["foo"] = new Dictionary { ["bad"] = "baz", ["0"] = "bar", @@ -3128,12 +3128,12 @@ public void ShouldTransformArraysToObjects() Qs.Decode("foo[0][a]=a&foo[0][b]=b&foo[1][a]=aa&foo[1][b]=bb", options) .Should() .BeEquivalentTo( - new Dictionary + new Dictionary { ["foo"] = new List { - new Dictionary { ["a"] = "a", ["b"] = "b" }, - new Dictionary { ["a"] = "aa", ["b"] = "bb" } + new Dictionary { ["a"] = "a", ["b"] = "b" }, + new Dictionary { ["a"] = "aa", ["b"] = "bb" } } } ); @@ -3147,28 +3147,28 @@ public void ShouldTransformArraysToObjectsWithDotNotation() Qs.Decode("foo[0].baz=bar&fool.bad=baz", optionsAllowDots) .Should() .BeEquivalentTo( - new Dictionary + new Dictionary { ["foo"] = new List { - new Dictionary { ["baz"] = "bar" } + new Dictionary { ["baz"] = "bar" } }, - ["fool"] = new Dictionary { ["bad"] = "baz" } + ["fool"] = new Dictionary { ["bad"] = "baz" } } ); Qs.Decode("foo[0].baz=bar&fool.bad.boo=baz", optionsAllowDots) .Should() .BeEquivalentTo( - new Dictionary + new Dictionary { ["foo"] = new List { - new Dictionary { ["baz"] = "bar" } + new Dictionary { ["baz"] = "bar" } }, - ["fool"] = new Dictionary + ["fool"] = new Dictionary { - ["bad"] = new Dictionary { ["boo"] = "baz" } + ["bad"] = new Dictionary { ["boo"] = "baz" } } } ); @@ -3176,24 +3176,24 @@ public void ShouldTransformArraysToObjectsWithDotNotation() Qs.Decode("foo[0][0].baz=bar&fool.bad=baz", optionsAllowDots) .Should() .BeEquivalentTo( - new Dictionary + new Dictionary { ["foo"] = new List { - new List { new Dictionary { ["baz"] = "bar" } } + new List { new Dictionary { ["baz"] = "bar" } } }, - ["fool"] = new Dictionary { ["bad"] = "baz" } + ["fool"] = new Dictionary { ["bad"] = "baz" } } ); Qs.Decode("foo[0].baz[0]=15&foo[0].bar=2", optionsAllowDots) .Should() .BeEquivalentTo( - new Dictionary + new Dictionary { ["foo"] = new List { - new Dictionary + new Dictionary { ["baz"] = new List { "15" }, ["bar"] = "2" @@ -3205,11 +3205,11 @@ public void ShouldTransformArraysToObjectsWithDotNotation() Qs.Decode("foo[0].baz[0]=15&foo[0].baz[1]=16&foo[0].bar=2", optionsAllowDots) .Should() .BeEquivalentTo( - new Dictionary + new Dictionary { ["foo"] = new List { - new Dictionary + new Dictionary { ["baz"] = new List { "15", "16" }, ["bar"] = "2" @@ -3221,36 +3221,36 @@ public void ShouldTransformArraysToObjectsWithDotNotation() Qs.Decode("foo.bad=baz&foo[0]=bar", optionsAllowDots) .Should() .BeEquivalentTo( - new Dictionary + new Dictionary { - ["foo"] = new Dictionary { ["bad"] = "baz", ["0"] = "bar" } + ["foo"] = new Dictionary { ["bad"] = "baz", ["0"] = "bar" } } ); Qs.Decode("foo.bad=baz&foo[]=bar", optionsAllowDots) .Should() .BeEquivalentTo( - new Dictionary + new Dictionary { - ["foo"] = new Dictionary { ["bad"] = "baz", ["0"] = "bar" } + ["foo"] = new Dictionary { ["bad"] = "baz", ["0"] = "bar" } } ); Qs.Decode("foo[]=bar&foo.bad=baz", optionsAllowDots) .Should() .BeEquivalentTo( - new Dictionary + new Dictionary { - ["foo"] = new Dictionary { ["0"] = "bar", ["bad"] = "baz" } + ["foo"] = new Dictionary { ["0"] = "bar", ["bad"] = "baz" } } ); Qs.Decode("foo.bad=baz&foo[]=bar&foo[]=foo", optionsAllowDots) .Should() .BeEquivalentTo( - new Dictionary + new Dictionary { - ["foo"] = new Dictionary + ["foo"] = new Dictionary { ["bad"] = "baz", ["0"] = "bar", @@ -3262,12 +3262,12 @@ public void ShouldTransformArraysToObjectsWithDotNotation() Qs.Decode("foo[0].a=a&foo[0].b=b&foo[1].a=aa&foo[1].b=bb", optionsAllowDots) .Should() .BeEquivalentTo( - new Dictionary + new Dictionary { ["foo"] = new List { - new Dictionary { ["a"] = "a", ["b"] = "b" }, - new Dictionary { ["a"] = "aa", ["b"] = "bb" } + new Dictionary { ["a"] = "a", ["b"] = "b" }, + new Dictionary { ["a"] = "aa", ["b"] = "bb" } } } ); @@ -3281,9 +3281,9 @@ public void ShouldCorrectlyPruneUndefinedValues() Qs.Decode("a[2]=b&a[99999999]=c", options) .Should() .BeEquivalentTo( - new Dictionary + new Dictionary { - ["a"] = new Dictionary { ["2"] = "b", ["99999999"] = "c" } + ["a"] = new Dictionary { ["2"] = "b", ["99999999"] = "c" } } ); } @@ -3296,15 +3296,15 @@ public void ShouldSupportMalformedUriCharacters() Qs.Decode("{%:%}", optionsStrictNullHandling) .Should() - .BeEquivalentTo(new Dictionary { ["{%:%}"] = null }); + .BeEquivalentTo(new Dictionary { ["{%:%}"] = null }); Qs.Decode("{%:%}=", options) .Should() - .BeEquivalentTo(new Dictionary { ["{%:%}"] = "" }); + .BeEquivalentTo(new Dictionary { ["{%:%}"] = "" }); Qs.Decode("foo=%:%}", options) .Should() - .BeEquivalentTo(new Dictionary { ["foo"] = "%:%}" }); + .BeEquivalentTo(new Dictionary { ["foo"] = "%:%}" }); } [Fact] @@ -3314,7 +3314,7 @@ public void ShouldNotProduceEmptyKeys() Qs.Decode("_r=1&", options) .Should() - .BeEquivalentTo(new Dictionary { ["_r"] = "1" }); + .BeEquivalentTo(new Dictionary { ["_r"] = "1" }); } [Fact] @@ -3325,18 +3325,18 @@ public void ShouldParseArraysOfObjects() Qs.Decode("a[][b]=c", options) .Should() .BeEquivalentTo( - new Dictionary + new Dictionary { - ["a"] = new List { new Dictionary { ["b"] = "c" } } + ["a"] = new List { new Dictionary { ["b"] = "c" } } } ); Qs.Decode("a[0][b]=c", options) .Should() .BeEquivalentTo( - new Dictionary + new Dictionary { - ["a"] = new List { new Dictionary { ["b"] = "c" } } + ["a"] = new List { new Dictionary { ["b"] = "c" } } } ); } @@ -3359,7 +3359,7 @@ public void ShouldAllowForEmptyStringsInArrays() Qs.Decode("a[]=b&a[]=&a[]=c", options) .Should() .BeEquivalentTo( - new Dictionary + new Dictionary { ["a"] = new List { "b", "", "c" } } @@ -3368,7 +3368,7 @@ public void ShouldAllowForEmptyStringsInArrays() Qs.Decode("a[0]=b&a[1]&a[2]=c&a[19]=", optionsStrictNullHandling20) .Should() .BeEquivalentTo( - new Dictionary + new Dictionary { ["a"] = new List { "b", null, "c", "" } } @@ -3377,7 +3377,7 @@ public void ShouldAllowForEmptyStringsInArrays() Qs.Decode("a[]=b&a[]&a[]=c&a[]=", optionsStrictNullHandling0) .Should() .BeEquivalentTo( - new Dictionary + new Dictionary { ["a"] = new List { "b", null, "c", "" } } @@ -3386,7 +3386,7 @@ public void ShouldAllowForEmptyStringsInArrays() Qs.Decode("a[0]=b&a[1]=&a[2]=c&a[19]", optionsStrictNullHandling20) .Should() .BeEquivalentTo( - new Dictionary + new Dictionary { ["a"] = new List { "b", "", "c", null } } @@ -3395,7 +3395,7 @@ public void ShouldAllowForEmptyStringsInArrays() Qs.Decode("a[]=b&a[]=&a[]=c&a[]", optionsStrictNullHandling0) .Should() .BeEquivalentTo( - new Dictionary + new Dictionary { ["a"] = new List { "b", "", "c", null } } @@ -3404,7 +3404,7 @@ public void ShouldAllowForEmptyStringsInArrays() Qs.Decode("a[]=&a[]=b&a[]=c", optionsStrictNullHandling0) .Should() .BeEquivalentTo( - new Dictionary + new Dictionary { ["a"] = new List { "", "b", "c" } } @@ -3419,7 +3419,7 @@ public void ShouldCompactSparseArrays() Qs.Decode("a[10]=1&a[2]=2", options) .Should() .BeEquivalentTo( - new Dictionary + new Dictionary { ["a"] = new List { "2", "1" } } @@ -3428,15 +3428,15 @@ public void ShouldCompactSparseArrays() Qs.Decode("a[1][b][2][c]=1", options) .Should() .BeEquivalentTo( - new Dictionary + new Dictionary { ["a"] = new List { - new Dictionary + new Dictionary { ["b"] = new List { - new Dictionary { ["c"] = "1" } + new Dictionary { ["c"] = "1" } } } } @@ -3446,13 +3446,13 @@ public void ShouldCompactSparseArrays() Qs.Decode("a[1][2][3][c]=1", options) .Should() .BeEquivalentTo( - new Dictionary + new Dictionary { ["a"] = new List { new List { - new List { new Dictionary { ["c"] = "1" } } + new List { new Dictionary { ["c"] = "1" } } } } } @@ -3461,7 +3461,7 @@ public void ShouldCompactSparseArrays() Qs.Decode("a[1][2][3][c][1]=1", options) .Should() .BeEquivalentTo( - new Dictionary + new Dictionary { ["a"] = new List { @@ -3469,7 +3469,7 @@ public void ShouldCompactSparseArrays() { new List { - new Dictionary + new Dictionary { ["c"] = new List { "1" } } @@ -3488,7 +3488,7 @@ public void ShouldParseSparseArrays() Qs.Decode("a[4]=1&a[1]=2", optionsAllowSparse) .Should() .BeEquivalentTo( - new Dictionary + new Dictionary { ["a"] = new List { null, "2", null, null, "1" } } @@ -3497,18 +3497,18 @@ public void ShouldParseSparseArrays() Qs.Decode("a[1][b][2][c]=1", optionsAllowSparse) .Should() .BeEquivalentTo( - new Dictionary + new Dictionary { ["a"] = new List { null, - new Dictionary + new Dictionary { ["b"] = new List { null, null, - new Dictionary { ["c"] = "1" } + new Dictionary { ["c"] = "1" } } } } @@ -3518,7 +3518,7 @@ public void ShouldParseSparseArrays() Qs.Decode("a[1][2][3][c]=1", optionsAllowSparse) .Should() .BeEquivalentTo( - new Dictionary + new Dictionary { ["a"] = new List { @@ -3532,7 +3532,7 @@ public void ShouldParseSparseArrays() null, null, null, - new Dictionary { ["c"] = "1" } + new Dictionary { ["c"] = "1" } } } } @@ -3542,7 +3542,7 @@ public void ShouldParseSparseArrays() Qs.Decode("a[1][2][3][c][1]=1", optionsAllowSparse) .Should() .BeEquivalentTo( - new Dictionary + new Dictionary { ["a"] = new List { @@ -3556,7 +3556,7 @@ public void ShouldParseSparseArrays() null, null, null, - new Dictionary + new Dictionary { ["c"] = new List { null, "1" } } @@ -3578,7 +3578,7 @@ public void ShouldParseJQueryParamStrings() ) .Should() .BeEquivalentTo( - new Dictionary + new Dictionary { ["filter"] = new List { @@ -3598,15 +3598,15 @@ public void ShouldContinueParsingWhenNoParentIsFound() Qs.Decode("[]=&a=b", options) .Should() - .BeEquivalentTo(new Dictionary { ["0"] = "", ["a"] = "b" }); + .BeEquivalentTo(new Dictionary { ["0"] = "", ["a"] = "b" }); Qs.Decode("[]&a=b", optionsStrictNullHandling) .Should() - .BeEquivalentTo(new Dictionary { ["0"] = null, ["a"] = "b" }); + .BeEquivalentTo(new Dictionary { ["0"] = null, ["a"] = "b" }); Qs.Decode("[foo]=bar", optionsStrictNullHandling) .Should() - .BeEquivalentTo(new Dictionary { ["foo"] = "bar" }); + .BeEquivalentTo(new Dictionary { ["foo"] = "bar" }); } [Fact] @@ -3630,11 +3630,11 @@ public void ShouldParseAStringWithAnAlternativeStringDelimiter() Qs.Decode("a=b;c=d", optionsSemicolon) .Should() - .BeEquivalentTo(new Dictionary { ["a"] = "b", ["c"] = "d" }); + .BeEquivalentTo(new Dictionary { ["a"] = "b", ["c"] = "d" }); Qs.Decode("a=b; c=d", optionsRegex) .Should() - .BeEquivalentTo(new Dictionary { ["a"] = "b", ["c"] = "d" }); + .BeEquivalentTo(new Dictionary { ["a"] = "b", ["c"] = "d" }); } [Fact] @@ -3645,11 +3645,11 @@ public void ShouldAllowOverridingParameterLimit() Qs.Decode("a=b&c=d", options1) .Should() - .BeEquivalentTo(new Dictionary { ["a"] = "b" }); + .BeEquivalentTo(new Dictionary { ["a"] = "b" }); Qs.Decode("a=b&c=d", optionsMax) .Should() - .BeEquivalentTo(new Dictionary { ["a"] = "b", ["c"] = "d" }); + .BeEquivalentTo(new Dictionary { ["a"] = "b", ["c"] = "d" }); } [Fact] @@ -3660,27 +3660,27 @@ public void ShouldAllowOverridingListLimit() Qs.Decode("a[0]=b", optionsNegative) .Should() .BeEquivalentTo( - new Dictionary + new Dictionary { - ["a"] = new Dictionary { ["0"] = "b" } + ["a"] = new Dictionary { ["0"] = "b" } } ); Qs.Decode("a[-1]=b", optionsNegative) .Should() .BeEquivalentTo( - new Dictionary + new Dictionary { - ["a"] = new Dictionary { ["-1"] = "b" } + ["a"] = new Dictionary { ["-1"] = "b" } } ); Qs.Decode("a[0]=b&a[1]=c", optionsNegative) .Should() .BeEquivalentTo( - new Dictionary + new Dictionary { - ["a"] = new Dictionary { ["0"] = "b", ["1"] = "c" } + ["a"] = new Dictionary { ["0"] = "b", ["1"] = "c" } } ); } @@ -3693,18 +3693,18 @@ public void ShouldAllowDisablingListParsing() Qs.Decode("a[0]=b&a[1]=c", options) .Should() .BeEquivalentTo( - new Dictionary + new Dictionary { - ["a"] = new Dictionary { ["0"] = "b", ["1"] = "c" } + ["a"] = new Dictionary { ["0"] = "b", ["1"] = "c" } } ); Qs.Decode("a[]=b", options) .Should() .BeEquivalentTo( - new Dictionary + new Dictionary { - ["a"] = new Dictionary { ["0"] = "b" } + ["a"] = new Dictionary { ["0"] = "b" } } ); } @@ -3717,15 +3717,15 @@ public void ShouldAllowForQueryStringPrefix() Qs.Decode("?foo=bar", optionsIgnorePrefix) .Should() - .BeEquivalentTo(new Dictionary { ["foo"] = "bar" }); + .BeEquivalentTo(new Dictionary { ["foo"] = "bar" }); Qs.Decode("foo=bar", optionsIgnorePrefix) .Should() - .BeEquivalentTo(new Dictionary { ["foo"] = "bar" }); + .BeEquivalentTo(new Dictionary { ["foo"] = "bar" }); Qs.Decode("?foo=bar", optionsKeepPrefix) .Should() - .BeEquivalentTo(new Dictionary { ["?foo"] = "bar" }); + .BeEquivalentTo(new Dictionary { ["?foo"] = "bar" }); } [Fact] @@ -3738,7 +3738,7 @@ public void ShouldParseStringWithCommaAsArrayDivider() Qs.Decode("foo=bar,tee", commaOptions) .Should() .BeEquivalentTo( - new Dictionary + new Dictionary { ["foo"] = new List { "bar", "tee" } } @@ -3747,9 +3747,9 @@ public void ShouldParseStringWithCommaAsArrayDivider() Qs.Decode("foo[bar]=coffee,tee", commaOptions) .Should() .BeEquivalentTo( - new Dictionary + new Dictionary { - ["foo"] = new Dictionary + ["foo"] = new Dictionary { ["bar"] = new List { "coffee", "tee" } } @@ -3758,32 +3758,32 @@ public void ShouldParseStringWithCommaAsArrayDivider() Qs.Decode("foo=", commaOptions) .Should() - .BeEquivalentTo(new Dictionary { ["foo"] = "" }); + .BeEquivalentTo(new Dictionary { ["foo"] = "" }); Qs.Decode("foo", commaOptions) .Should() - .BeEquivalentTo(new Dictionary { ["foo"] = "" }); + .BeEquivalentTo(new Dictionary { ["foo"] = "" }); Qs.Decode("foo", commaStrictNullOptions) .Should() - .BeEquivalentTo(new Dictionary { ["foo"] = null }); + .BeEquivalentTo(new Dictionary { ["foo"] = null }); Qs.Decode("a[0]=c", simpleOptions) .Should() - .BeEquivalentTo(new Dictionary { ["a"] = new List { "c" } }); + .BeEquivalentTo(new Dictionary { ["a"] = new List { "c" } }); Qs.Decode("a[]=c", simpleOptions) .Should() - .BeEquivalentTo(new Dictionary { ["a"] = new List { "c" } }); + .BeEquivalentTo(new Dictionary { ["a"] = new List { "c" } }); Qs.Decode("a[]=c", commaOptions) .Should() - .BeEquivalentTo(new Dictionary { ["a"] = new List { "c" } }); + .BeEquivalentTo(new Dictionary { ["a"] = new List { "c" } }); Qs.Decode("a[0]=c&a[1]=d", simpleOptions) .Should() .BeEquivalentTo( - new Dictionary + new Dictionary { ["a"] = new List { "c", "d" } } @@ -3792,7 +3792,7 @@ public void ShouldParseStringWithCommaAsArrayDivider() Qs.Decode("a[]=c&a[]=d", simpleOptions) .Should() .BeEquivalentTo( - new Dictionary + new Dictionary { ["a"] = new List { "c", "d" } } @@ -3801,7 +3801,7 @@ public void ShouldParseStringWithCommaAsArrayDivider() Qs.Decode("a[]=c&a[]=d", commaOptions) .Should() .BeEquivalentTo( - new Dictionary + new Dictionary { ["a"] = new List { "c", "d" } } @@ -3823,15 +3823,15 @@ public void ShouldUseNumberDecoder() Qs.Decode("foo=1", options) .Should() - .BeEquivalentTo(new Dictionary { ["foo"] = "[1]" }); + .BeEquivalentTo(new Dictionary { ["foo"] = "[1]" }); Qs.Decode("foo=1.0", options) .Should() - .BeEquivalentTo(new Dictionary { ["foo"] = "1.0" }); + .BeEquivalentTo(new Dictionary { ["foo"] = "1.0" }); Qs.Decode("foo=0", options) .Should() - .BeEquivalentTo(new Dictionary { ["foo"] = "[0]" }); + .BeEquivalentTo(new Dictionary { ["foo"] = "[0]" }); } [Fact] @@ -3841,12 +3841,12 @@ public void ShouldParseCommaDelimitedArray() Qs.Decode("foo=a%2Cb", options) .Should() - .BeEquivalentTo(new Dictionary { ["foo"] = "a,b" }); + .BeEquivalentTo(new Dictionary { ["foo"] = "a,b" }); Qs.Decode("foo=a%2C%20b,d", options) .Should() .BeEquivalentTo( - new Dictionary + new Dictionary { ["foo"] = new List { "a, b", "d" } } @@ -3855,7 +3855,7 @@ public void ShouldParseCommaDelimitedArray() Qs.Decode("foo=a%2C%20b,c%2C%20d", options) .Should() .BeEquivalentTo( - new Dictionary + new Dictionary { ["foo"] = new List { "a, b", "c, d" } } @@ -3873,13 +3873,13 @@ public void ShouldNotCrashWhenParsingDeepObjects() str += "=bar"; - Dictionary? result = null; + Dictionary? result = null; var action = () => { result = Qs.Decode(str, options); }; action.Should().NotThrow(); var depth = 0; var current = result?["foo"]; - while (current is Dictionary dict && dict.ContainsKey("p")) + while (current is Dictionary dict && dict.ContainsKey("p")) { current = dict["p"]; depth++; @@ -3895,15 +3895,15 @@ public void ShouldHandleParamsStartingWithAClosingBracket() Qs.Decode("]=toString", options) .Should() - .BeEquivalentTo(new Dictionary { ["]"] = "toString" }); + .BeEquivalentTo(new Dictionary { ["]"] = "toString" }); Qs.Decode("]]=toString", options) .Should() - .BeEquivalentTo(new Dictionary { ["]]"] = "toString" }); + .BeEquivalentTo(new Dictionary { ["]]"] = "toString" }); Qs.Decode("]hello]=toString", options) .Should() - .BeEquivalentTo(new Dictionary { ["]hello]"] = "toString" }); + .BeEquivalentTo(new Dictionary { ["]hello]"] = "toString" }); } [Fact] @@ -3913,15 +3913,15 @@ public void ShouldHandleParamsStartingWithAStartingBracket() Qs.Decode("[=toString", options) .Should() - .BeEquivalentTo(new Dictionary { ["["] = "toString" }); + .BeEquivalentTo(new Dictionary { ["["] = "toString" }); Qs.Decode("[[=toString", options) .Should() - .BeEquivalentTo(new Dictionary { ["[["] = "toString" }); + .BeEquivalentTo(new Dictionary { ["[["] = "toString" }); Qs.Decode("[hello[=toString", options) .Should() - .BeEquivalentTo(new Dictionary { ["[hello["] = "toString" }); + .BeEquivalentTo(new Dictionary { ["[hello["] = "toString" }); } [Fact] @@ -3932,9 +3932,9 @@ public void ShouldAddKeysToObjects() Qs.Decode("a[b]=c&a=d", options) .Should() .BeEquivalentTo( - new Dictionary + new Dictionary { - ["a"] = new Dictionary { ["b"] = "c", ["d"] = true } + ["a"] = new Dictionary { ["b"] = "c", ["d"] = true } } ); } @@ -3964,7 +3964,7 @@ public void ShouldParseWithCustomEncoding() // Note: This test may need adjustment based on actual Shift_JIS encoding behavior Qs.Decode("%8c%a7=%91%e5%8d%e3%95%7b", options) .Should() - .BeEquivalentTo(new Dictionary { ["県"] = "大阪府" }); + .BeEquivalentTo(new Dictionary { ["県"] = "大阪府" }); } [Fact] @@ -3974,7 +3974,7 @@ public void ShouldParseOtherCharset() Qs.Decode("%A2=%BD", options) .Should() - .BeEquivalentTo(new Dictionary { ["¢"] = "½" }); + .BeEquivalentTo(new Dictionary { ["¢"] = "½" }); } [Fact] @@ -3993,36 +3993,36 @@ public void ShouldParseCharsetSentinel() optionsIso ) .Should() - .BeEquivalentTo(new Dictionary { ["ø"] = "ø" }); + .BeEquivalentTo(new Dictionary { ["ø"] = "ø" }); Qs.Decode( $"utf8={urlEncodedNumCheckmark}&{urlEncodedOSlashInUtf8}={urlEncodedOSlashInUtf8}", optionsUtf8 ) .Should() - .BeEquivalentTo(new Dictionary { ["ø"] = "ø" }); + .BeEquivalentTo(new Dictionary { ["ø"] = "ø" }); Qs.Decode($"a={urlEncodedOSlashInUtf8}&utf8={urlEncodedNumCheckmark}", optionsUtf8) .Should() - .BeEquivalentTo(new Dictionary { ["a"] = "ø" }); + .BeEquivalentTo(new Dictionary { ["a"] = "ø" }); Qs.Decode($"utf8=foo&{urlEncodedOSlashInUtf8}={urlEncodedOSlashInUtf8}", optionsUtf8) .Should() - .BeEquivalentTo(new Dictionary { ["ø"] = "ø" }); + .BeEquivalentTo(new Dictionary { ["ø"] = "ø" }); Qs.Decode( $"utf8={urlEncodedCheckmarkInUtf8}&{urlEncodedOSlashInUtf8}={urlEncodedOSlashInUtf8}", optionsDefault ) .Should() - .BeEquivalentTo(new Dictionary { ["ø"] = "ø" }); + .BeEquivalentTo(new Dictionary { ["ø"] = "ø" }); Qs.Decode( $"utf8={urlEncodedNumCheckmark}&{urlEncodedOSlashInUtf8}={urlEncodedOSlashInUtf8}", optionsDefault ) .Should() - .BeEquivalentTo(new Dictionary { ["ø"] = "ø" }); + .BeEquivalentTo(new Dictionary { ["ø"] = "ø" }); } [Fact] @@ -4044,15 +4044,15 @@ public void ShouldInterpretNumericEntities() Qs.Decode($"foo={urlEncodedNumSmiley}", optionsIsoInterpret) .Should() - .BeEquivalentTo(new Dictionary { ["foo"] = "☺" }); + .BeEquivalentTo(new Dictionary { ["foo"] = "☺" }); Qs.Decode($"foo={urlEncodedNumSmiley}", optionsIso) .Should() - .BeEquivalentTo(new Dictionary { ["foo"] = "☺" }); + .BeEquivalentTo(new Dictionary { ["foo"] = "☺" }); Qs.Decode($"foo={urlEncodedNumSmiley}", optionsUtfInterpret) .Should() - .BeEquivalentTo(new Dictionary { ["foo"] = "☺" }); + .BeEquivalentTo(new Dictionary { ["foo"] = "☺" }); } [Fact] @@ -4062,7 +4062,7 @@ public void ShouldAllowForDecodingKeysAndValues() Qs.Decode("KeY=vAlUe", options) .Should() - .BeEquivalentTo(new Dictionary { ["key"] = "value" }); + .BeEquivalentTo(new Dictionary { ["key"] = "value" }); } [Fact] @@ -4073,12 +4073,12 @@ public void ShouldHandleProofOfConcept() Qs.Decode("filters[name][:eq]=John&filters[age][:ge]=18&filters[age][:le]=60", options) .Should() .BeEquivalentTo( - new Dictionary + new Dictionary { - ["filters"] = new Dictionary + ["filters"] = new Dictionary { - ["name"] = new Dictionary { [":eq"] = "John" }, - ["age"] = new Dictionary + ["name"] = new Dictionary { [":eq"] = "John" }, + ["age"] = new Dictionary { [":ge"] = "18", [":le"] = "60" @@ -4121,6 +4121,6 @@ public void Decode_NonGeneric_Hashtable_Is_Normalised() var decoded = Qs.Decode(raw); // Assert – keys are strings and values preserved - decoded.Should().Equal(new Dictionary { ["x"] = 1, ["2"] = "y" }); + decoded.Should().Equal(new Dictionary { ["x"] = 1, ["2"] = "y" }); } } \ No newline at end of file diff --git a/QsNet.Tests/ExampleTests.cs b/QsNet.Tests/ExampleTests.cs index 7ef4247..6847b37 100644 --- a/QsNet.Tests/ExampleTests.cs +++ b/QsNet.Tests/ExampleTests.cs @@ -14,7 +14,7 @@ public class ExampleTests public void SimpleExamples_DecodesSimpleQueryString() { // Act & Assert - Qs.Decode("a=c").Should().BeEquivalentTo(new Dictionary { ["a"] = "c" }); + Qs.Decode("a=c").Should().BeEquivalentTo(new Dictionary { ["a"] = "c" }); } [Fact] @@ -31,9 +31,9 @@ public void Decoding_Maps_AllowsNestedMaps() Qs.Decode("foo[bar]=baz") .Should() .BeEquivalentTo( - new Dictionary + new Dictionary { - ["foo"] = new Dictionary { ["bar"] = "baz" } + ["foo"] = new Dictionary { ["bar"] = "baz" } } ); } @@ -45,9 +45,9 @@ public void Decoding_Maps_WorksWithUriEncodedStrings() Qs.Decode("a%5Bb%5D=c") .Should() .BeEquivalentTo( - new Dictionary + new Dictionary { - ["a"] = new Dictionary { ["b"] = "c" } + ["a"] = new Dictionary { ["b"] = "c" } } ); } @@ -59,11 +59,11 @@ public void Decoding_Maps_CanNestMapsDeep() Qs.Decode("foo[bar][baz]=foobarbaz") .Should() .BeEquivalentTo( - new Dictionary + new Dictionary { - ["foo"] = new Dictionary + ["foo"] = new Dictionary { - ["bar"] = new Dictionary { ["baz"] = "foobarbaz" } + ["bar"] = new Dictionary { ["baz"] = "foobarbaz" } } } ); @@ -76,19 +76,19 @@ public void Decoding_Maps_OnlyDecodesUpTo5ChildrenDeepByDefault() Qs.Decode("a[b][c][d][e][f][g][h][i]=j") .Should() .BeEquivalentTo( - new Dictionary + new Dictionary { - ["a"] = new Dictionary + ["a"] = new Dictionary { - ["b"] = new Dictionary + ["b"] = new Dictionary { - ["c"] = new Dictionary + ["c"] = new Dictionary { - ["d"] = new Dictionary + ["d"] = new Dictionary { - ["e"] = new Dictionary + ["e"] = new Dictionary { - ["f"] = new Dictionary + ["f"] = new Dictionary { ["[g][h][i]"] = "j" } @@ -108,11 +108,11 @@ public void Decoding_Maps_CanOverrideDepthWithDecodeOptions() Qs.Decode("a[b][c][d][e][f][g][h][i]=j", new DecodeOptions { Depth = 1 }) .Should() .BeEquivalentTo( - new Dictionary + new Dictionary { - ["a"] = new Dictionary + ["a"] = new Dictionary { - ["b"] = new Dictionary { ["[c][d][e][f][g][h][i]"] = "j" } + ["b"] = new Dictionary { ["[c][d][e][f][g][h][i]"] = "j" } } } ); @@ -124,7 +124,7 @@ public void Decoding_Maps_OnlyParsesUpTo1000ParametersByDefault() // Act & Assert Qs.Decode("a=b&c=d", new DecodeOptions { ParameterLimit = 1 }) .Should() - .BeEquivalentTo(new Dictionary { ["a"] = "b" }); + .BeEquivalentTo(new Dictionary { ["a"] = "b" }); } [Fact] @@ -133,7 +133,7 @@ public void Decoding_Maps_CanBypassLeadingQuestionMarkWithIgnoreQueryPrefix() // Act & Assert Qs.Decode("?a=b&c=d", new DecodeOptions { IgnoreQueryPrefix = true }) .Should() - .BeEquivalentTo(new Dictionary { ["a"] = "b", ["c"] = "d" }); + .BeEquivalentTo(new Dictionary { ["a"] = "b", ["c"] = "d" }); } [Fact] @@ -142,7 +142,7 @@ public void Decoding_Maps_AcceptsCustomDelimiter() // Act & Assert Qs.Decode("a=b;c=d", new DecodeOptions { Delimiter = new StringDelimiter(";") }) .Should() - .BeEquivalentTo(new Dictionary { ["a"] = "b", ["c"] = "d" }); + .BeEquivalentTo(new Dictionary { ["a"] = "b", ["c"] = "d" }); } [Fact] @@ -151,7 +151,7 @@ public void Decoding_Maps_AcceptsRegexDelimiter() // Act & Assert Qs.Decode("a=b;c=d", new DecodeOptions { Delimiter = new RegexDelimiter("[;,]") }) .Should() - .BeEquivalentTo(new Dictionary { ["a"] = "b", ["c"] = "d" }); + .BeEquivalentTo(new Dictionary { ["a"] = "b", ["c"] = "d" }); } [Fact] @@ -161,9 +161,9 @@ public void Decoding_Maps_CanEnableDotNotationWithAllowDots() Qs.Decode("a.b=c", new DecodeOptions { AllowDots = true }) .Should() .BeEquivalentTo( - new Dictionary + new Dictionary { - ["a"] = new Dictionary { ["b"] = "c" } + ["a"] = new Dictionary { ["b"] = "c" } } ); } @@ -178,9 +178,9 @@ public void Decoding_Maps_CanDecodeDotsInKeysWithDecodeDotInKeys() ) .Should() .BeEquivalentTo( - new Dictionary + new Dictionary { - ["name.obj"] = new Dictionary + ["name.obj"] = new Dictionary { ["first"] = "John", ["last"] = "Doe" @@ -196,7 +196,7 @@ public void Decoding_Maps_CanAllowEmptyListsWithAllowEmptyLists() Qs.Decode("foo[]&bar=baz", new DecodeOptions { AllowEmptyLists = true }) .Should() .BeEquivalentTo( - new Dictionary { ["foo"] = new List(), ["bar"] = "baz" } + new Dictionary { ["foo"] = new List(), ["bar"] = "baz" } ); } @@ -207,7 +207,7 @@ public void Decoding_Maps_HandlesDuplicateKeysByDefault() Qs.Decode("foo=bar&foo=baz") .Should() .BeEquivalentTo( - new Dictionary + new Dictionary { ["foo"] = new List { "bar", "baz" } } @@ -221,7 +221,7 @@ public void Decoding_Maps_CanCombineDuplicatesExplicitly() Qs.Decode("foo=bar&foo=baz", new DecodeOptions { Duplicates = Duplicates.Combine }) .Should() .BeEquivalentTo( - new Dictionary + new Dictionary { ["foo"] = new List { "bar", "baz" } } @@ -234,7 +234,7 @@ public void Decoding_Maps_CanTakeFirstDuplicateWithDuplicatesFirst() // Act & Assert Qs.Decode("foo=bar&foo=baz", new DecodeOptions { Duplicates = Duplicates.First }) .Should() - .BeEquivalentTo(new Dictionary { ["foo"] = "bar" }); + .BeEquivalentTo(new Dictionary { ["foo"] = "bar" }); } [Fact] @@ -243,7 +243,7 @@ public void Decoding_Maps_CanTakeLastDuplicateWithDuplicatesLast() // Act & Assert Qs.Decode("foo=bar&foo=baz", new DecodeOptions { Duplicates = Duplicates.Last }) .Should() - .BeEquivalentTo(new Dictionary { ["foo"] = "baz" }); + .BeEquivalentTo(new Dictionary { ["foo"] = "baz" }); } [Fact] @@ -252,7 +252,7 @@ public void Decoding_Maps_SupportsLatin1CharsetForLegacyBrowsers() // Act & Assert Qs.Decode("a=%A7", new DecodeOptions { Charset = Encoding.Latin1 }) .Should() - .BeEquivalentTo(new Dictionary { ["a"] = "§" }); + .BeEquivalentTo(new Dictionary { ["a"] = "§" }); } [Fact] @@ -264,7 +264,7 @@ public void Decoding_Maps_SupportsCharsetSentinelWithLatin1() new DecodeOptions { Charset = Encoding.Latin1, CharsetSentinel = true } ) .Should() - .BeEquivalentTo(new Dictionary { ["a"] = "ø" }); + .BeEquivalentTo(new Dictionary { ["a"] = "ø" }); } [Fact] @@ -276,7 +276,7 @@ public void Decoding_Maps_SupportsCharsetSentinelWithUtf8() new DecodeOptions { Charset = Encoding.UTF8, CharsetSentinel = true } ) .Should() - .BeEquivalentTo(new Dictionary { ["a"] = "ø" }); + .BeEquivalentTo(new Dictionary { ["a"] = "ø" }); } [Fact] @@ -288,7 +288,7 @@ public void Decoding_Maps_CanInterpretNumericEntities() new DecodeOptions { Charset = Encoding.Latin1, InterpretNumericEntities = true } ) .Should() - .BeEquivalentTo(new Dictionary { ["a"] = "☺" }); + .BeEquivalentTo(new Dictionary { ["a"] = "☺" }); } [Fact] @@ -298,7 +298,7 @@ public void Decoding_Lists_CanParseListsUsingBracketNotation() Qs.Decode("a[]=b&a[]=c") .Should() .BeEquivalentTo( - new Dictionary + new Dictionary { ["a"] = new List { "b", "c" } } @@ -312,7 +312,7 @@ public void Decoding_Lists_CanSpecifyAnIndex() Qs.Decode("a[1]=c&a[0]=b") .Should() .BeEquivalentTo( - new Dictionary + new Dictionary { ["a"] = new List { "b", "c" } } @@ -326,7 +326,7 @@ public void Decoding_Lists_CompactsSparseListsPreservingOrder() Qs.Decode("a[1]=b&a[15]=c") .Should() .BeEquivalentTo( - new Dictionary + new Dictionary { ["a"] = new List { "b", "c" } } @@ -340,7 +340,7 @@ public void Decoding_Lists_PreservesEmptyStringValues() Qs.Decode("a[]=&a[]=b") .Should() .BeEquivalentTo( - new Dictionary + new Dictionary { ["a"] = new List { "", "b" } } @@ -349,7 +349,7 @@ public void Decoding_Lists_PreservesEmptyStringValues() Qs.Decode("a[0]=b&a[1]=&a[2]=c") .Should() .BeEquivalentTo( - new Dictionary + new Dictionary { ["a"] = new List { "b", "", "c" } } @@ -363,9 +363,9 @@ public void Decoding_Lists_ConvertsHighIndicesToMapKeys() Qs.Decode("a[100]=b") .Should() .BeEquivalentTo( - new Dictionary + new Dictionary { - ["a"] = new Dictionary { ["100"] = "b" } + ["a"] = new Dictionary { ["100"] = "b" } } ); } @@ -377,9 +377,9 @@ public void Decoding_Lists_CanOverrideListLimit() Qs.Decode("a[1]=b", new DecodeOptions { ListLimit = 0 }) .Should() .BeEquivalentTo( - new Dictionary + new Dictionary { - ["a"] = new Dictionary { ["1"] = "b" } + ["a"] = new Dictionary { ["1"] = "b" } } ); } @@ -391,9 +391,9 @@ public void Decoding_Lists_CanDisableListParsingEntirely() Qs.Decode("a[]=b", new DecodeOptions { ParseLists = false }) .Should() .BeEquivalentTo( - new Dictionary + new Dictionary { - ["a"] = new Dictionary { ["0"] = "b" } + ["a"] = new Dictionary { ["0"] = "b" } } ); } @@ -405,9 +405,9 @@ public void Decoding_Lists_MergesMixedNotationsIntoMap() Qs.Decode("a[0]=b&a[b]=c") .Should() .BeEquivalentTo( - new Dictionary + new Dictionary { - ["a"] = new Dictionary { ["0"] = "b", ["b"] = "c" } + ["a"] = new Dictionary { ["0"] = "b", ["b"] = "c" } } ); } @@ -419,9 +419,9 @@ public void Decoding_Lists_CanCreateListsOfMaps() Qs.Decode("a[][b]=c") .Should() .BeEquivalentTo( - new Dictionary + new Dictionary { - ["a"] = new List { new Dictionary { ["b"] = "c" } } + ["a"] = new List { new Dictionary { ["b"] = "c" } } } ); } @@ -433,7 +433,7 @@ public void Decoding_Lists_CanParseCommaSeparatedValues() Qs.Decode("a=b,c", new DecodeOptions { Comma = true }) .Should() .BeEquivalentTo( - new Dictionary + new Dictionary { ["a"] = new List { "b", "c" } } @@ -447,7 +447,7 @@ public void Decoding_PrimitiveValues_ParsesAllValuesAsStringsByDefault() Qs.Decode("a=15&b=true&c=null") .Should() .BeEquivalentTo( - new Dictionary + new Dictionary { ["a"] = "15", ["b"] = "true", @@ -861,7 +861,7 @@ public void NullValues_DoesNotDistinguishBetweenParametersWithAndWithoutEqualSig // Act & Assert Qs.Decode("a&b=") .Should() - .BeEquivalentTo(new Dictionary { ["a"] = "", ["b"] = "" }); + .BeEquivalentTo(new Dictionary { ["a"] = "", ["b"] = "" }); } [Fact] @@ -882,7 +882,7 @@ public void NullValues_CanDecodeValuesWithoutEqualsBackToNullUsingStrictNullHand // Act & Assert Qs.Decode("a&b=", new DecodeOptions { StrictNullHandling = true }) .Should() - .BeEquivalentTo(new Dictionary { ["a"] = null, ["b"] = "" }); + .BeEquivalentTo(new Dictionary { ["a"] = null, ["b"] = "" }); } [Fact] @@ -984,7 +984,7 @@ public void Charset_CanUseCustomDecoderForDifferentCharacterSets() } ) .Should() - .BeEquivalentTo(new Dictionary { ["a"] = "hello" }); + .BeEquivalentTo(new Dictionary { ["a"] = "hello" }); } [Fact] diff --git a/QsNet.Tests/QsNet.Tests.csproj b/QsNet.Tests/QsNet.Tests.csproj index f19a1f4..d9472f0 100644 --- a/QsNet.Tests/QsNet.Tests.csproj +++ b/QsNet.Tests/QsNet.Tests.csproj @@ -17,7 +17,7 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive - + diff --git a/QsNet/Extensions.cs b/QsNet/Extensions.cs index 3474684..e2e5a80 100644 --- a/QsNet/Extensions.cs +++ b/QsNet/Extensions.cs @@ -18,7 +18,7 @@ public static class Extensions /// The query string to decode /// Optional decoder settings /// A Dictionary containing the decoded key-value pairs - public static Dictionary ToQueryMap( + public static Dictionary ToQueryMap( this string queryString, DecodeOptions? options = null ) diff --git a/QsNet/Internal/Utils.cs b/QsNet/Internal/Utils.cs index e124c21..469fa02 100644 --- a/QsNet/Internal/Utils.cs +++ b/QsNet/Internal/Utils.cs @@ -858,7 +858,27 @@ IDictionary dictionary /// internal static Dictionary ConvertNestedDictionary(IDictionary dict) { - ConvertNestedValues(dict); + return ConvertNestedDictionary(dict, new HashSet(ReferenceEqualityComparer.Instance)); + } + + private static Dictionary ConvertNestedDictionary( + IDictionary dict, + ISet visited + ) + { + // If we've already seen this dictionary, don't descend again. + // If it's already string-keyed, just return it to preserve identity. + if (!visited.Add(dict)) + { + if (dict is Dictionary sk) + return sk; + + // Fallback: make a shallow string-keyed view without descending + var shallow = new Dictionary(dict.Count); + foreach (DictionaryEntry de in dict) + shallow[de.Key?.ToString() ?? string.Empty] = de.Value; + return shallow; + } var result = new Dictionary(dict.Count); @@ -869,14 +889,37 @@ IDictionary dictionary switch (item) { + case IDictionary child when ReferenceEquals(child, dict): + // Direct self-reference: keep the same instance to preserve identity + item = child; + break; + + case IDictionary child and Dictionary: + // User-supplied string-keyed map: preserve identity, do not recurse + item = child; + break; + case IDictionary child: - item = ConvertNestedDictionary(child); + // Non-string-keyed (e.g., object-keyed) map: convert recursively + item = ConvertNestedDictionary(child, visited); break; case IList list: + // Convert IDictionary children inside lists, but preserve string-keyed identity for (var i = 0; i < list.Count; i++) - if (list[i] is IDictionary inner) - list[i] = ConvertNestedDictionary(inner); + { + if (list[i] is not IDictionary inner) continue; + if (inner is Dictionary) + { + // keep as-is + } + else + { + list[i] = ConvertNestedDictionary(inner, visited); + } + } + + item = list; break; } @@ -904,4 +947,110 @@ private static object NormalizeForTarget(IDictionary map) copy[k] = map[k]; return copy; } + + /// + /// Converts a nested structure with object keys to a Dictionary with string keys. + /// This method performs a deep conversion, handling circular references and preserving identity. + /// + /// + /// + /// + internal static Dictionary ToStringKeyDeepNonRecursive(object root) + { + if (root is not IDictionary srcRoot) + throw new ArgumentException("Root must be an IDictionary", nameof(root)); + + var visited = new Dictionary(ReferenceEqualityComparer.Instance); + var stack = new Stack<(object src, object dst)>(); + + var top = new Dictionary(srcRoot.Count); + visited[srcRoot] = top; + stack.Push((srcRoot, top)); + + while (stack.Count > 0) + { + var (src, dst) = stack.Pop(); + + if (src is IDictionary sd && dst is Dictionary dd) + foreach (DictionaryEntry de in sd) + { + var key = de.Key?.ToString() ?? string.Empty; + var val = de.Value; + + switch (val) + { + case IDictionary child: + // Preserve identity for already string-keyed child maps + if (child is Dictionary sk) + { + dd[key] = sk; + // register so future references reuse this instance + if (!visited.ContainsKey(child)) visited[child] = sk; + break; + } + + if (visited.TryGetValue(child, out var existing)) + { + dd[key] = existing; + } + else + { + var newChild = new Dictionary(child.Count); + dd[key] = newChild; + visited[child] = newChild; + stack.Push((child, newChild)); + } + + break; + + case IList list: + if (visited.TryGetValue(list, out var existingList)) + { + dd[key] = existingList; + break; + } + + var newList = new List(list.Count); + dd[key] = newList; + visited[list] = newList; + + for (var i = 0; i < list.Count; i++) + { + var item = list[i]; + if (item is IDictionary inner) + { + if (inner is Dictionary innerSk) + { + newList.Add(innerSk); + if (!visited.ContainsKey(inner)) visited[inner] = innerSk; + } + else if (visited.TryGetValue(inner, out var ex)) + { + newList.Add(ex); + } + else + { + var newInner = new Dictionary(inner.Count); + newList.Add(newInner); + visited[inner] = newInner; + stack.Push((inner, newInner)); + } + } + else + { + newList.Add(item); + } + } + + break; + + default: + dd[key] = val; + break; + } + } + } + + return top; + } } \ No newline at end of file diff --git a/QsNet/Qs.cs b/QsNet/Qs.cs index 2f5ccef..c779585 100644 --- a/QsNet/Qs.cs +++ b/QsNet/Qs.cs @@ -26,7 +26,7 @@ public static class Qs /// The decoded Dictionary /// If the input is not a string or Dictionary /// If limits are exceeded and ThrowOnLimitExceeded is true - public static Dictionary Decode(object? input, DecodeOptions? options = null) + public static Dictionary Decode(object? input, DecodeOptions? options = null) { var opts = options ?? new DecodeOptions(); @@ -34,7 +34,7 @@ public static class Qs throw new ArgumentException("The input must be a String or a Map"); if (input is null or string { Length: 0 } or IDictionary { Count: 0 }) - return new Dictionary(); + return new Dictionary(); // parse the raw pairs (string-keyed) var tempObj = input switch @@ -55,10 +55,11 @@ public static class Qs if (opts is { ParseLists: true, ListLimit: > 0 } && (tempObj?.Count ?? 0) > opts.ListLimit) finalOptions = opts.CopyWith(parseLists: false); + // keep internal work in object-keyed maps var obj = new Dictionary(); if (tempObj is not { Count: > 0 }) - return Utils.Compact(obj, opts.AllowSparseLists); // overload below + return new Dictionary(); foreach (var (key, value) in tempObj) { @@ -82,7 +83,9 @@ public static class Qs }; } - return Utils.Compact(obj, opts.AllowSparseLists); + // compact (still object-keyed), then convert the whole tree to string-keyed + var compacted = Utils.Compact(obj, opts.AllowSparseLists); + return Utils.ToStringKeyDeepNonRecursive(compacted); } /// From e6c2ed8c4593bceb786e42bb8c6e5aa87f0e0357 Mon Sep 17 00:00:00 2001 From: Klemen Tusar Date: Wed, 13 Aug 2025 12:38:45 +0200 Subject: [PATCH 3/3] :children_crossing: standardize test dictionary keys to `string` for consistency across tests --- QsNet.Tests/Fixtures/Data/EmptyTestCases.cs | 14 +++--- QsNet.Tests/UtilsTests.cs | 56 ++++++++++----------- 2 files changed, 35 insertions(+), 35 deletions(-) diff --git a/QsNet.Tests/Fixtures/Data/EmptyTestCases.cs b/QsNet.Tests/Fixtures/Data/EmptyTestCases.cs index be8aeb7..56624f5 100644 --- a/QsNet.Tests/Fixtures/Data/EmptyTestCases.cs +++ b/QsNet.Tests/Fixtures/Data/EmptyTestCases.cs @@ -330,10 +330,10 @@ internal static class EmptyTestCases ["indices"] = "[0]=a&[1]=b& [0]=1", ["repeat"] = "=a&=b& =1" }, - ["noEmptyKeys"] = new Dictionary + ["noEmptyKeys"] = new Dictionary { - [0] = "a", - [1] = "b", + ["0"] = "a", + ["1"] = "b", [" "] = new List { "1" } } }, @@ -345,10 +345,10 @@ internal static class EmptyTestCases [""] = new List { "a", "b" }, ["a"] = new List { "1", "2" } }, - ["noEmptyKeys"] = new Dictionary + ["noEmptyKeys"] = new Dictionary { - [0] = "a", - [1] = "b", + ["0"] = "a", + ["1"] = "b", ["a"] = new List { "1", "2" } }, ["stringifyOutput"] = new Dictionary @@ -392,7 +392,7 @@ internal static class EmptyTestCases ["indices"] = "[0]=a&[1]=b", ["repeat"] = "=a&=b" }, - ["noEmptyKeys"] = new Dictionary { [0] = "a", [1] = "b" } + ["noEmptyKeys"] = new Dictionary { ["0"] = "a", ["1"] = "b" } } ]; } \ No newline at end of file diff --git a/QsNet.Tests/UtilsTests.cs b/QsNet.Tests/UtilsTests.cs index d83161e..4ac8641 100644 --- a/QsNet.Tests/UtilsTests.cs +++ b/QsNet.Tests/UtilsTests.cs @@ -512,9 +512,9 @@ public void Unescape_HandlesBadHexAfterPercent() [Fact] public void Merge_MapWithList() { - var target = new Dictionary { { "0", "a" } }; + var target = new Dictionary { { "0", "a" } }; var source = new List { Undefined.Create(), "b" }; - var expected = new Dictionary { { "0", "a" }, { "1", "b" } }; + var expected = new Dictionary { { "0", "a" }, { "1", "b" } }; Utils.Merge(target, source).Should().BeEquivalentTo(expected); } @@ -691,7 +691,7 @@ public void Merge_TwoObjectsWithSameKey() new Dictionary { { "a", "b" } }, new Dictionary { { "a", "c" } } ); - var expected = new Dictionary + var expected = new Dictionary { { "a", @@ -719,7 +719,7 @@ public void Merge_StandaloneAndObjectIntoList() } } ); - var expected = new Dictionary + var expected = new Dictionary { { "foo", @@ -757,11 +757,11 @@ public void Merge_StandaloneAndTwoObjectsIntoList() } } ); - var expected = new Dictionary + var expected = new Dictionary { { "foo", - new Dictionary + new Dictionary { { "0", "bar" }, { @@ -793,7 +793,7 @@ public void Merge_ObjectSandwichedByTwoStandalonesIntoList() }, new Dictionary { { "foo", "baz" } } ); - var expected = new Dictionary + var expected = new Dictionary { { "foo", @@ -837,7 +837,7 @@ public void Merge_TwoListsIntoList() } } ); - var expected2 = new Dictionary + var expected2 = new Dictionary { { "foo", @@ -880,7 +880,7 @@ public void Merge_TwoSetsIntoSet() } } ); - var expected2 = new Dictionary + var expected2 = new Dictionary { { "foo", @@ -914,7 +914,7 @@ public void Merge_SetIntoList() } } ); - var expected = new Dictionary + var expected = new Dictionary { { "foo", @@ -948,7 +948,7 @@ public void Merge_ListIntoSet() } } ); - var expected = new Dictionary + var expected = new Dictionary { { "foo", @@ -982,7 +982,7 @@ public void Merge_SetIntoListWithMultipleElements() } } ); - var expected = new Dictionary + var expected = new Dictionary { { "foo", @@ -1016,11 +1016,11 @@ public void Merge_ObjectIntoList() } } ); - var expected = new Dictionary + var expected = new Dictionary { { "foo", - new Dictionary { { "0", "bar" }, { "baz", "xyzzy" } } + new Dictionary { { "0", "bar" }, { "baz", "xyzzy" } } } }; @@ -1050,11 +1050,11 @@ public void Merge_ListIntoObject() } } ); - var expected = new Dictionary + var expected = new Dictionary { { "foo", - new Dictionary { { "bar", "baz" }, { "0", "xyzzy" } } + new Dictionary { { "bar", "baz" }, { "0", "xyzzy" } } } }; @@ -1086,7 +1086,7 @@ public void Merge_SetWithUndefinedWithAnotherSet() } } ); - var expected1 = new Dictionary + var expected1 = new Dictionary { { "foo", @@ -1116,7 +1116,7 @@ public void Merge_SetWithUndefinedWithAnotherSet() } } ); - var expected2 = new Dictionary + var expected2 = new Dictionary { { "foo", @@ -1162,7 +1162,7 @@ public void Merge_SetOfMapsWithAnotherSetOfMaps() } } ); - var expected2 = new Dictionary + var expected2 = new Dictionary { { "foo", @@ -1330,13 +1330,13 @@ public void IsEmpty_EmptyCollectionsAndMaps() public void ToObjectKeyedDictionary_Converts_NonGeneric_IDictionary() { // arrange - IDictionary src = new Hashtable { ["a"] = 1, [2] = "b" }; + IDictionary src = new Hashtable { ["a"] = 1, ["2"] = "b" }; // act var result = Utils.ToObjectKeyedDictionary(src); // assert – same contents, but a different instance - result.Should().Equal(new Dictionary { ["a"] = 1, [2] = "b" }); + result.Should().Equal(new Dictionary { ["a"] = 1, ["2"] = "b" }); // cast to object so the types match the generic constraint Assert.NotSame(src, result); @@ -1400,7 +1400,7 @@ public void ConvertNestedValues_Deep_Walks_And_Preserves_Cycles() var inner = new Dictionary(); inner["self"] = inner; // cycle - var root = new Dictionary { ["k"] = inner }; + var root = new Dictionary { ["k"] = inner }; Utils.ConvertNestedValues(root); @@ -1468,7 +1468,7 @@ public void Merge_MapsAndArrays() .Merge(dict1, dict2) .Should() .BeEquivalentTo( - new Dictionary + new Dictionary { { "a", @@ -1480,7 +1480,7 @@ public void Merge_MapsAndArrays() .Merge(dict1, dict3) .Should() .BeEquivalentTo( - new Dictionary + new Dictionary { { "a", @@ -1512,11 +1512,11 @@ public void Merge_MapsAndArrays() } }; - var expected1 = new Dictionary + var expected1 = new Dictionary { { "foo", - new Dictionary + new Dictionary { { "0", "bar" }, { @@ -1547,7 +1547,7 @@ public void Merge_MapsAndArrays() .Merge(a, b) .Should() .BeEquivalentTo( - new Dictionary + new Dictionary { { "foo", @@ -1560,6 +1560,6 @@ public void Merge_MapsAndArrays() Utils .Merge(x, "bar") .Should() - .BeEquivalentTo(new Dictionary { { "foo", "baz" }, { "bar", true } }); + .BeEquivalentTo(new Dictionary { { "foo", "baz" }, { "bar", true } }); } } \ No newline at end of file