diff --git a/lib/src/extensions/decode.dart b/lib/src/extensions/decode.dart index f16fee8..2b9b365 100644 --- a/lib/src/extensions/decode.dart +++ b/lib/src/extensions/decode.dart @@ -13,11 +13,11 @@ extension _$Decode on QS { ? val.split(',') : val; - static Map _parseQueryStringValues( + static Map _parseQueryStringValues( String str, [ DecodeOptions options = const DecodeOptions(), ]) { - final Map obj = {}; + final Map obj = {}; final String cleanStr = options.ignoreQueryPrefix ? str.replaceFirst('?', '') : str; @@ -107,7 +107,7 @@ extension _$Decode on QS { ? List.empty(growable: true) : [if (leaf is Iterable) ...leaf else leaf]; } else { - obj = Map.of({}); + obj = {}; final String cleanRoot = root.startsWith('[') && root.endsWith(']') ? root.slice(1, root.length - 1) : root; @@ -116,7 +116,7 @@ extension _$Decode on QS { : cleanRoot; final int? index = int.tryParse(decodedRoot); if (!options.parseLists && decodedRoot == '') { - obj = Map.of({0: leaf}); + obj = {'0': leaf}; } else if (index != null && index >= 0 && root != decodedRoot && @@ -130,7 +130,7 @@ extension _$Decode on QS { ); obj[index] = leaf; } else { - obj[index ?? decodedRoot] = leaf; + obj[index?.toString() ?? decodedRoot] = leaf; } } diff --git a/lib/src/methods.dart b/lib/src/methods.dart index 1e30737..b4b535f 100644 --- a/lib/src/methods.dart +++ b/lib/src/methods.dart @@ -3,7 +3,7 @@ import 'package:qs_dart/src/models/encode_options.dart'; import 'package:qs_dart/src/qs.dart'; /// Convenience method for [QS.decode] -Map decode( +Map decode( dynamic input, [ DecodeOptions options = const DecodeOptions(), ]) => diff --git a/lib/src/qs.dart b/lib/src/qs.dart index 9cb90e3..bd37331 100644 --- a/lib/src/qs.dart +++ b/lib/src/qs.dart @@ -19,43 +19,45 @@ part 'extensions/encode.dart'; /// A query string decoder (parser) and encoder (stringifier) class. final class QS { - /// Decodes a [String] or [Map] into a [Map]. + /// Decodes a [String] or [Map] into a [Map]. /// Providing custom [options] will override the default behavior. - static Map decode( + static Map decode( dynamic input, [ DecodeOptions options = const DecodeOptions(), ]) { - if (!(input is String? || input is Map?)) { + if (!(input is String? || input is Map?)) { throw ArgumentError.value( input, 'input', - 'The input must be a String or a Map', + 'The input must be a String or a Map', ); } if (input?.isEmpty ?? true) { - return Map.of({}); + return {}; } - Map? tempObj = input is String + Map? tempObj = input is String ? _$Decode._parseQueryStringValues(input, options) : input; - Map obj = {}; + Map obj = {}; // Iterate over the keys and setup the new object - for (final MapEntry entry in tempObj?.entries ?? List.empty()) { - final newObj = _$Decode._parseKeys( - entry.key, - entry.value, - options, - input is String, - ); - - obj = Utils.merge( - obj, - newObj, - options, - ); + if (tempObj?.isNotEmpty ?? false) { + for (final MapEntry entry in tempObj!.entries) { + final newObj = _$Decode._parseKeys( + entry.key, + entry.value, + options, + input is String, + ); + + obj = Utils.merge( + obj, + newObj, + options, + ); + } } return Utils.compact(obj); diff --git a/lib/src/utils.dart b/lib/src/utils.dart index 98018fb..b6ad780 100644 --- a/lib/src/utils.dart +++ b/lib/src/utils.dart @@ -87,11 +87,12 @@ final class Utils { } } else if (target is Map) { if (source is Iterable) { - target = Map.of(target) - ..addAll({ - for (final (int i, dynamic item) in source.indexed) - if (item is! Undefined) i: item - }); + target = { + for (final MapEntry entry in target.entries) + entry.key.toString(): entry.value, + for (final (int i, dynamic item) in source.indexed) + if (item is! Undefined) i.toString(): item + }; } } else if (source != null) { if (target is! Iterable && source is Iterable) { @@ -105,9 +106,9 @@ final class Utils { if (target == null || target is! Map) { if (target is Iterable) { - return Map.of({ + return Map.of({ for (final (int i, dynamic item) in target.indexed) - if (item is! Undefined) i: item, + if (item is! Undefined) i.toString(): item, ...source, }); } @@ -124,13 +125,19 @@ final class Utils { ]; } - Map mergeTarget = target is Iterable && source is! Iterable - ? (target as Iterable).toList().whereNotUndefined().asMap() - : Map.of(target); + Map mergeTarget = target is Iterable && source is! Iterable + ? { + for (final (int i, dynamic item) in (target as Iterable).indexed) + if (item is! Undefined) i.toString(): item + } + : { + for (final MapEntry entry in target.entries) + entry.key.toString(): entry.value + }; return source.entries.fold(mergeTarget, (Map acc, MapEntry entry) { acc.update( - entry.key, + entry.key.toString(), (value) => merge( value, entry.value, @@ -332,8 +339,8 @@ final class Utils { } } - static Map compact(Map value) { - final List queue = [ + static Map compact(Map value) { + final List> queue = [ { 'obj': {'o': value}, 'prop': 'o', diff --git a/test/fixtures/data/empty_test_cases.dart b/test/fixtures/data/empty_test_cases.dart index cf6a74a..930dfb4 100644 --- a/test/fixtures/data/empty_test_cases.dart +++ b/test/fixtures/data/empty_test_cases.dart @@ -244,8 +244,8 @@ const List> emptyTestCases = [ 'repeat': '=a&=b& =1' }, 'noEmptyKeys': { - 0: 'a', - 1: 'b', + '0': 'a', + '1': 'b', ' ': ['1'] } }, @@ -256,8 +256,8 @@ const List> emptyTestCases = [ 'a': ['1', '2'] }, 'noEmptyKeys': { - 0: 'a', - 1: 'b', + '0': 'a', + '1': 'b', 'a': ['1', '2'] }, 'stringifyOutput': { @@ -292,6 +292,6 @@ const List> emptyTestCases = [ 'indices': '[0]=a&[1]=b', 'repeat': '=a&=b' }, - 'noEmptyKeys': {0: 'a', 1: 'b'} + 'noEmptyKeys': {'0': 'a', '1': 'b'} } ]; diff --git a/test/unit/array_test.dart b/test/unit/array_test.dart index e2731f6..747e648 100644 --- a/test/unit/array_test.dart +++ b/test/unit/array_test.dart @@ -7,17 +7,21 @@ import 'package:test/test.dart'; void main() { group('SplayTreeMap', () { test('indices are ordered in value', () { - final SplayTreeMap array = - SplayTreeMap.from({1: 'a', 0: 'b', 2: 'c'}); + final SplayTreeMap array = + SplayTreeMap.from({ + '1': 'a', + '0': 'b', + '2': 'c', + }); expect(array.values, ['b', 'a', 'c']); }); test('indices are ordered in value 2', () { - final SplayTreeMap array = SplayTreeMap(); - array[1] = 'c'; - array[0] = 'b'; - array[2] = 'd'; + final SplayTreeMap array = SplayTreeMap(); + array['1'] = 'c'; + array['0'] = 'b'; + array['2'] = 'd'; expect(array.values, ['b', 'c', 'd']); }); diff --git a/test/unit/decode_test.dart b/test/unit/decode_test.dart index f948871..1e54804 100644 --- a/test/unit/decode_test.dart +++ b/test/unit/decode_test.dart @@ -17,7 +17,7 @@ void main() { }); test('parses a simple string', () { - expect(QS.decode('0=foo'), equals({0: 'foo'})); + expect(QS.decode('0=foo'), equals({'0': 'foo'})); expect(QS.decode('foo=c++'), equals({'foo': 'c '})); expect( QS.decode('a[>=]=23'), @@ -471,7 +471,7 @@ void main() { expect( QS.decode('a[1]=c', const DecodeOptions(listLimit: 0)), equals({ - 'a': {1: 'c'} + 'a': {'1': 'c'} }), ); expect( @@ -492,7 +492,7 @@ void main() { expect( QS.decode('a[21]=a', const DecodeOptions(listLimit: 20)), equals({ - 'a': {21: 'a'} + 'a': {'21': 'a'} }), ); @@ -505,7 +505,7 @@ void main() { expect( QS.decode('a[21]=a'), equals({ - 'a': {21: 'a'} + 'a': {'21': 'a'} }), ); }); @@ -561,31 +561,31 @@ void main() { expect( QS.decode('foo[0]=bar&foo[bad]=baz'), equals({ - 'foo': {0: 'bar', 'bad': 'baz'} + 'foo': {'0': 'bar', 'bad': 'baz'} }), ); expect( QS.decode('foo[bad]=baz&foo[0]=bar'), equals({ - 'foo': {'bad': 'baz', 0: 'bar'} + 'foo': {'bad': 'baz', '0': 'bar'} }), ); expect( QS.decode('foo[bad]=baz&foo[]=bar'), equals({ - 'foo': {'bad': 'baz', 0: 'bar'} + 'foo': {'bad': 'baz', '0': 'bar'} }), ); expect( QS.decode('foo[]=bar&foo[bad]=baz'), equals({ - 'foo': {0: 'bar', 'bad': 'baz'} + 'foo': {'0': 'bar', 'bad': 'baz'} }), ); expect( QS.decode('foo[bad]=baz&foo[]=bar&foo[]=foo'), equals({ - 'foo': {'bad': 'baz', 0: 'bar', 1: 'foo'} + 'foo': {'bad': 'baz', '0': 'bar', '1': 'foo'} }), ); expect( @@ -664,28 +664,28 @@ void main() { QS.decode( 'foo.bad=baz&foo[0]=bar', const DecodeOptions(allowDots: true)), equals({ - 'foo': {'bad': 'baz', 0: 'bar'} + 'foo': {'bad': 'baz', '0': 'bar'} }), ); expect( QS.decode( 'foo.bad=baz&foo[]=bar', const DecodeOptions(allowDots: true)), equals({ - 'foo': {'bad': 'baz', 0: 'bar'} + 'foo': {'bad': 'baz', '0': 'bar'} }), ); expect( QS.decode( 'foo[]=bar&foo.bad=baz', const DecodeOptions(allowDots: true)), equals({ - 'foo': {0: 'bar', 'bad': 'baz'} + 'foo': {'0': 'bar', 'bad': 'baz'} }), ); expect( QS.decode('foo.bad=baz&foo[]=bar&foo[]=foo', const DecodeOptions(allowDots: true)), equals({ - 'foo': {'bad': 'baz', 0: 'bar', 1: 'foo'} + 'foo': {'bad': 'baz', '0': 'bar', '1': 'foo'} }), ); expect( @@ -705,7 +705,7 @@ void main() { expect( QS.decode('a[2]=b&a[99999999]=c'), equals({ - 'a': {2: 'b', 99999999: 'c'} + 'a': {'2': 'b', '99999999': 'c'} }), ); }); @@ -889,13 +889,13 @@ void main() { }); test('continues parsing when no parent is found', () { - expect(QS.decode('[]=&a=b'), equals({0: '', 'a': 'b'})); + expect(QS.decode('[]=&a=b'), equals({'0': '', 'a': 'b'})); expect( QS.decode( '[]&a=b', const DecodeOptions(strictNullHandling: true), ), - equals({0: null, 'a': 'b'}), + equals({'0': null, 'a': 'b'}), ); expect(QS.decode('[foo]=bar'), equals({'foo': 'bar'})); }); @@ -945,7 +945,7 @@ void main() { expect( QS.decode('a[0]=b', const DecodeOptions(listLimit: -1)), equals({ - 'a': {0: 'b'} + 'a': {'0': 'b'} }), ); expect( @@ -958,26 +958,26 @@ void main() { expect( QS.decode('a[-1]=b', const DecodeOptions(listLimit: -1)), equals({ - 'a': {-1: 'b'} + 'a': {'-1': 'b'} }), ); expect( QS.decode('a[-1]=b', const DecodeOptions(listLimit: 0)), equals({ - 'a': {-1: 'b'} + 'a': {'-1': 'b'} }), ); expect( QS.decode('a[0]=b&a[1]=c', const DecodeOptions(listLimit: -1)), equals({ - 'a': {0: 'b', 1: 'c'} + 'a': {'0': 'b', '1': 'c'} }), ); expect( QS.decode('a[0]=b&a[1]=c', const DecodeOptions(listLimit: 0)), equals({ - 'a': {0: 'b', 1: 'c'} + 'a': {'0': 'b', '1': 'c'} }), ); }); @@ -989,7 +989,7 @@ void main() { const DecodeOptions(parseLists: false), ), equals({ - 'a': {0: 'b', 1: 'c'} + 'a': {'0': 'b', '1': 'c'} }), ); expect( @@ -998,7 +998,7 @@ void main() { const DecodeOptions(parseLists: false), ), equals({ - 'a': {0: 'b'} + 'a': {'0': 'b'} }), ); }); @@ -1339,7 +1339,7 @@ void main() { final Map expectedlist = {}; expectedlist['a'] = {}; - expectedlist['a'][0] = 'b'; + expectedlist['a']['0'] = 'b'; expectedlist['a']['c'] = 'd'; expect( QS.decode('a[]=b&a[c]=d'), diff --git a/test/unit/utils_test.dart b/test/unit/utils_test.dart index 84e7165..0136725 100644 --- a/test/unit/utils_test.dart +++ b/test/unit/utils_test.dart @@ -182,8 +182,8 @@ void main() { group('merge', () { test('merges SplayTreeMap with List', () { expect( - Utils.merge({0: 'a'}, [const Undefined(), 'b']), - equals({0: 'a', 1: 'b'}), + Utils.merge({'0': 'a'}, [const Undefined(), 'b']), + equals({'0': 'a', '1': 'b'}), ); }); @@ -369,8 +369,8 @@ void main() { equals( { 'foo': { - 0: 'bar', - 1: {'first': '123'}, + '0': 'bar', + '1': {'first': '123'}, 'second': '456' } }, @@ -631,7 +631,7 @@ void main() { ), equals( { - 'foo': {0: 'bar', 'baz': 'xyzzy'}, + 'foo': {'0': 'bar', 'baz': 'xyzzy'}, }, ), ); @@ -671,7 +671,7 @@ void main() { ), equals( { - 'foo': {'bar': 'baz', 0: 'xyzzy'}, + 'foo': {'bar': 'baz', '0': 'xyzzy'}, }, ), );