decode static method
- dynamic input, [
- DecodeOptions? options
Decode a query string or a pre-parsed map into a structured map.
input
may be:- a query String (e.g.
"a=1&b[c]=2"
), or - a pre-tokenized
Map<String, dynamic>
produced by a custom tokenizer.
- a query String (e.g.
- When
input
isnull
or the empty string,{}
is returned. - If DecodeOptions.parseLists is
true
and the number of top‑level parameters exceeds DecodeOptions.listLimit, list parsing is temporarily disabled for this call to bound memory (mirrors Nodeqs
). - Throws ArgumentError if
input
is neither aString
nor aMap<String, dynamic>
.
See DecodeOptions for delimiter, nesting depth, numeric-entity handling, duplicates policy, and other knobs.
Implementation
static Map<String, dynamic> decode(dynamic input, [DecodeOptions? options]) {
options ??= const DecodeOptions();
// Default to the library's safe, Node-`qs` compatible settings.
// Fail fast on unsupported input shapes to avoid ambiguous behavior.
if (!(input is String? || input is Map<String, dynamic>?)) {
throw ArgumentError.value(
input,
'input',
'The input must be a String or a Map<String, dynamic>',
);
}
// Normalize `null` / empty string to an empty map.
if (input?.isEmpty ?? true) {
return <String, dynamic>{};
}
final Map<String, dynamic>? tempObj = input is String
? _$Decode._parseQueryStringValues(input, options)
: input;
// Guardrail: if the top-level parameter count is large, temporarily disable
// list parsing to keep memory bounded (matches Node `qs`).
if (options.parseLists &&
options.listLimit > 0 &&
(tempObj?.length ?? 0) > options.listLimit) {
options = options.copyWith(parseLists: false);
}
Map<String, dynamic> obj = {};
// Merge each parsed key into the accumulator using the same rules as Node `qs`.
// Iterate over the keys and setup the new object
if (tempObj?.isNotEmpty ?? false) {
for (final MapEntry<String, dynamic> entry in tempObj!.entries) {
final parsed = _$Decode._parseKeys(
entry.key, entry.value, options, input is String);
if (obj.isEmpty && parsed is Map<String, dynamic>) {
obj = parsed; // direct assignment – no merge needed
} else {
obj = Utils.merge(obj, parsed, options) as Map<String, dynamic>;
}
}
}
// Drop undefined/empty leaves to match the reference behavior.
return Utils.compact(obj);
}