{"id":15498090,"url":"https://github.com/techouse/qs","last_synced_at":"2025-10-09T08:32:41.390Z","repository":{"id":230983397,"uuid":"780657790","full_name":"techouse/qs","owner":"techouse","description":"A query string encoding and decoding library for Dart. Ported from qs for JavaScript.","archived":false,"fork":false,"pushed_at":"2025-09-26T22:15:32.000Z","size":626,"stargazers_count":6,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-10-09T08:32:06.603Z","etag":null,"topics":["dart","qs","query-encoding","query-parser","query-string","url-parsing","url-query"],"latest_commit_sha":null,"homepage":"https://techouse.github.io/qs/","language":"Dart","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-3-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/techouse.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":"CODE-OF-CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":".github/CODEOWNERS","security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null},"funding":{"github":"techouse","custom":["https://paypal.me/ktusar"]}},"created_at":"2024-04-01T23:14:00.000Z","updated_at":"2025-09-26T22:15:35.000Z","dependencies_parsed_at":"2024-08-12T16:49:57.913Z","dependency_job_id":"7ce153af-b708-4fa8-80f3-636f6b07c716","html_url":"https://github.com/techouse/qs","commit_stats":null,"previous_names":["techouse/qs"],"tags_count":44,"template":false,"template_full_name":null,"purl":"pkg:github/techouse/qs","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/techouse%2Fqs","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/techouse%2Fqs/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/techouse%2Fqs/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/techouse%2Fqs/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/techouse","download_url":"https://codeload.github.com/techouse/qs/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/techouse%2Fqs/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":279001051,"owners_count":26082991,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-10-09T02:00:07.460Z","response_time":59,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["dart","qs","query-encoding","query-parser","query-string","url-parsing","url-query"],"created_at":"2024-10-02T08:41:51.430Z","updated_at":"2025-10-09T08:32:41.384Z","avatar_url":"https://github.com/techouse.png","language":"Dart","readme":"# qs_dart\n\n\u003cp align=\"center\"\u003e\n    \u003cimg src=\"https://github.com/techouse/qs/raw/main/logo.png?raw=true?raw=true\" width=\"256\" alt=\"qs_dart\" /\u003e\n\u003c/p\u003e\n\nA query string encoding and decoding library for Dart.\n\nPorted from [qs](https://www.npmjs.com/package/qs) for JavaScript.\n\n[![Pub Version](https://img.shields.io/pub/v/qs_dart)](https://pub.dev/packages/qs_dart)\n[![Pub Publisher](https://img.shields.io/pub/publisher/qs_dart)](https://pub.dev/publishers/tusar.dev/packages)\n[![Pub Likes](https://img.shields.io/pub/likes/qs_dart)](https://pub.dev/packages/qs_dart/score)\n[![Pub Points](https://img.shields.io/pub/points/qs_dart)](https://pub.dev/packages/qs_dart/score)\n[![Pub Monthly Downloads](https://img.shields.io/pub/dm/qs_dart)](https://pub.dev/packages/qs_dart/score)\n[![PubStats Popularity](https://pubstats.dev/badges/packages/qs_dart/popularity.svg)](https://pubstats.dev/packages/qs_dart)\n[![PubStats Rank](https://pubstats.dev/badges/packages/qs_dart/rank.svg)](https://pubstats.dev/packages/qs_dart)\n[![PubStats Dependents](https://pubstats.dev/badges/packages/qs_dart/dependents.svg)](https://pubstats.dev/packages/qs_dart)\n[![Test](https://github.com/techouse/qs/actions/workflows/test.yml/badge.svg)](https://github.com/techouse/qs/actions/workflows/test.yml)\n[![codecov](https://codecov.io/gh/techouse/qs/graph/badge.svg?token=e8KkRgZzPf)](https://codecov.io/gh/techouse/qs)\n[![Codacy Badge](https://app.codacy.com/project/badge/Grade/3630ce1150f840e08c94f40754d24688)](https://app.codacy.com/gh/techouse/qs/dashboard?utm_source=gh\u0026utm_medium=referral\u0026utm_content=\u0026utm_campaign=Badge_grade)\n[![GitHub](https://img.shields.io/github/license/techouse/qs)](LICENSE)\n[![GitHub Sponsors](https://img.shields.io/github/sponsors/techouse)](https://github.com/sponsors/techouse)\n[![GitHub Repo stars](https://img.shields.io/github/stars/techouse/qs)](https://github.com/techouse/qs/stargazers)\n\n## Highlights\n\n- **Nested maps \u0026 lists**: `a[b][c]=d` ⇄ `{ 'a': { 'b': { 'c': 'd' } } }`; arrays via `a[]=b\u0026a[]=c` or `a[0]=b\u0026a[1]=c`.\n- **Multiple list formats**: `indices` (`a[0]=x`), `brackets` (`a[]=x`), `repeat` (`a=x\u0026a=y`), `comma` (`a=x,y`) with optional comma round‑trip.\n- **Dot notation**: parse/encode `a.b=c` as nested; options to encode or decode literal dots in keys (`encodeDotInKeys` / `decodeDotInKeys`).\n- **Charset \u0026 sentinel**: UTF‑8 (default) and Latin‑1; `charsetSentinel` (`utf8=✓`) to auto‑detect encoding.\n- **Hooks \u0026 controls**: custom `encoder`/`decoder`, encode keys‑only or values‑only, alternate delimiters (string or `RegExp`), ignore leading `?`.\n- **Duplicates \u0026 empties**: strategies for duplicates (`combine`/`first`/`last`); allow empty list items (`foo[]`) with `allowEmptyLists`.\n- **Null handling**: `strictNullHandling`, `skipNulls`, and clear empty‑string behavior.\n- **Safety limits**: configurable `depth`, `strictDepth`, `parameterLimit`, `listLimit`, and `parseLists` toggle.\n- **Dates \u0026 ordering**: pluggable `serializeDate`, stable key ordering with `sort`, selective output via `filter`.\n- **RFC modes**: spaces as `%20` (RFC 3986, default) or `+` (RFC 1738).\n- **Dart‑friendly**: `Uri.queryParametersQs()` and `Uri.toStringQs()` extensions for idiomatic usage in Dart/Flutter.\n\n## Installation\n\nAdd the package to your project:\n\n### For Dart projects:\n```sh\ndart pub add qs_dart\n```\n\n### For Flutter projects:\n```sh\nflutter pub add qs_dart\n```\n\n## Usage\n\nA simple usage example:\n\n```dart\nimport 'package:qs_dart/qs_dart.dart';\nimport 'package:test/test.dart';\n\nvoid main() {\n  test('Simple example', () {\n    expect(\n      QS.decode('a=c'),\n      equals({'a': 'c'}),\n    );\n\n    expect(\n      QS.encode({'a': 'c'}),\n      equals('a=c'),\n    );\n  });\n  \n  test('Uri Extension usage', () {\n    expect(\n      Uri.parse('https://test.local/example?a[b][c]=d').queryParametersQs(),\n      equals({\n        'a': {\n          'b': {'c': 'd'}\n        }\n      }),\n    );\n    \n    expect(\n      Uri.https('test.local', '/example', {'a': '1', 'b': '2'}).toStringQs(), \n      equals('https://test.local/example?a=1\u0026b=2'),\n    );\n  });\n}\n```\n\n### Decoding Maps\n\n```dart\nMap\u003cString, dynamic\u003e decode(\n  dynamic str, [\n  DecodeOptions options = const DecodeOptions(),\n]);\n```\n\n[decode] allows you to create nested [Map]s within your query strings, by surrounding the name of sub-keys with \nsquare brackets `[]`. For example, the string `'foo[bar]=baz'` converts to:\n\n```dart\nexpect(\n  QS.decode('foo[bar]=baz'),\n  equals({'foo': {'bar': 'baz'}}),\n);\n```\n\nURI encoded strings work too:\n\n```dart\nexpect(\n  QS.decode('a%5Bb%5D=c'),\n  equals({'a': {'b': 'c'}}),\n);\n```\n\nYou can also nest your [Map]s, like `'foo[bar][baz]=foobarbaz'`:\n\n```dart\nexpect(\n  QS.decode('foo[bar][baz]=foobarbaz'),\n  equals({'foo': {'bar': {'baz': 'foobarbaz'}}}),\n);\n```\n\nBy default, nested [Map]s are decoded up to 5 levels deep. For example, decoding\na string like `'a[b][c][d][e][f][g][h][i]=j'` yields the following [Map]:\n\n```dart\nexpect(\n  QS.decode('a[b][c][d][e][f][g][h][i]=j'),\n  equals({\n    'a': {\n      'b': {\n        'c': {\n          'd': {\n            'e': {\n              'f': {\n                '[g][h][i]': 'j'\n              }\n            }\n          }\n        }\n      }\n    }\n  }),\n);\n```\n\nYou can override this limit with [DecodeOptions.depth]:\n\n```dart\nexpect(\n  QS.decode(\n    'a[b][c][d][e][f][g][h][i]=j',\n    const DecodeOptions(depth: 1),\n  ),\n  equals({\n    'a': {\n      'b': {'[c][d][e][f][g][h][i]': 'j'},\n    },\n  }),\n);\n```\n\nYou can configure [decode] to throw an error when parsing nested input beyond this depth using\n[DecodeOptions.strictDepth] (defaults to false):\n\n```dart\nexpect(\n  () =\u003e QS.decode(\n    'a[b][c][d][e][f][g][h][i]=j',\n    const DecodeOptions(\n      depth: 1,\n      strictDepth: true,\n    ),\n  ),\n  throwsA(isA\u003cRangeError\u003e()),\n);\n```\n\nThe depth limit helps mitigate abuse when [decode] is used to parse user input, and it is recommended to keep it a\nreasonably small number. [DecodeOptions.strictDepth] adds a layer of protection by throwing a [RangeError] when the\nlimit is exceeded, allowing you to catch and handle such cases.\n\nFor similar reasons, by default [decode] parses up to **1000** parameters. Override with\n[DecodeOptions.parameterLimit]:\n\n```dart\nexpect(\n  QS.decode(\n    'a=b\u0026c=d',\n    const DecodeOptions(parameterLimit: 1),\n  ),\n  equals({'a': 'b'}),\n);\n```\n\nTo bypass the leading question mark, use [DecodeOptions.ignoreQueryPrefix]:\n\n```dart\nexpect(\n  QS.decode(\n    '?a=b\u0026c=d',\n    const DecodeOptions(ignoreQueryPrefix: true),\n  ),\n  equals(\n    {'a': 'b', 'c': 'd'},\n  ),\n);\n```\n\nYou can provide a custom [DecodeOptions.delimiter]:\n\n```dart\nexpect(\n  QS.decode(\n    'a=b;c=d',\n    const DecodeOptions(delimiter: ';'),\n  ),\n  equals({'a': 'b', 'c': 'd'}),\n);\n```\n\n[DecodeOptions.delimiter] can be a [RegExp] too:\n\n```dart\nexpect(\n  QS.decode(\n    'a=b;c=d',\n    DecodeOptions(delimiter: RegExp(r'[;,]')),\n  ),\n  equals({'a': 'b', 'c': 'd'}),\n);\n```\n\nEnable dot notation with [DecodeOptions.allowDots]:\n\n```dart\nexpect(\n  QS.decode(\n    'a.b=c',\n    const DecodeOptions(allowDots: true),\n  ),\n  equals({'a': {'b': 'c'}}),\n);\n```\n\nDecode percent-encoded dots in keys with [DecodeOptions.decodeDotInKeys].\n\n**Note:** it implies [DecodeOptions.allowDots], so [decode] will error if you set [DecodeOptions.decodeDotInKeys] \nto `true`, and `DecodeOptions.allowDots` to `false`.\n\n```dart\nexpect(\n  QS.decode(\n    'name%252Eobj.first=John\u0026name%252Eobj.last=Doe',\n    const DecodeOptions(decodeDotInKeys: true),\n  ),\n  equals({\n    'name.obj': {'first': 'John', 'last': 'Doe'}\n  }),\n);\n```\n\nAllow empty [List] values within a [Map] with [DecodeOptions.allowEmptyLists].\n\n```dart\nexpect(\n  QS.decode(\n    'foo[]\u0026bar=baz',\n    const DecodeOptions(allowEmptyLists: true),\n  ),\n  equals({\n    'foo': [],\n    'bar': 'baz',\n  }),\n);\n```\n\nOption [DecodeOptions.duplicates] can be used to change the behavior when duplicate keys are encountered.\n\n```dart\nexpect(\n  QS.decode('foo=bar\u0026foo=baz'),\n  equals({\n    'foo': ['bar', 'baz']\n  }),\n);\n\nexpect(\n  QS.decode(\n    'foo=bar\u0026foo=baz',\n    const DecodeOptions(duplicates: Duplicates.combine),\n  ),\n  equals({\n    'foo': ['bar', 'baz']\n  }),\n);\n\nexpect(\n  QS.decode(\n    'foo=bar\u0026foo=baz',\n    const DecodeOptions(duplicates: Duplicates.first),\n  ),\n  equals({'foo': 'bar'}),\n);\n\nexpect(\n  QS.decode(\n    'foo=bar\u0026foo=baz',\n    const DecodeOptions(duplicates: Duplicates.last),\n  ),\n  equals({'foo': 'baz'}),\n);\n```\n\nIf you have to deal with legacy browsers or services, there's also support for decoding percent-encoded octets as\n[latin1]:\n\n```dart\nexpect(\n  QS.decode(\n    'a=%A7',\n    const DecodeOptions(charset: latin1),\n  ),\n  equals({'a': '§'}),\n);\n```\n\nSome services add an initial `utf8=✓` value to forms so that old Internet Explorer versions are more likely to submit the\nform as utf-8. Additionally, the server can check the value against wrong encodings of the checkmark character and detect \nthat a query string or `application/x-www-form-urlencoded` body was *not* sent as utf-8, eg. if the form had an \n`accept-charset` parameter or the containing page had a different character set.\n\n**QS** supports this mechanism via the [DecodeOptions.charsetSentinel] option.\nIf specified, the `utf8` parameter will be omitted from the returned [Map].\nIt will be used to switch to [latin1] or [utf8] mode depending on how the checkmark is encoded.\n\n**Important**: When you specify both the [DecodeOptions.charset] option and the [DecodeOptions.charsetSentinel] option, \nthe [DecodeOptions.charset] will be overridden when the request contains a `utf8` parameter from which the actual charset \ncan be deduced. In that sense the [DecodeOptions.charset] will behave as the default charset rather than the authoritative \ncharset.\n\n```dart\nexpect(\n  QS.decode(\n    'utf8=%E2%9C%93\u0026a=%C3%B8',\n    const DecodeOptions(\n      charset: latin1,\n      charsetSentinel: true,\n    ),\n  ),\n  equals({'a': 'ø'}),\n);\n\nexpect(\n  QS.decode(\n    'utf8=%26%2310003%3B\u0026a=%F8',\n    const DecodeOptions(\n      charset: utf8,\n      charsetSentinel: true,\n    ),\n  ),\n  equals({'a': 'ø'}),\n);\n```\n\nIf you want to decode the [`\u0026#...;`](https://www.w3schools.com/html/html_entities.asp) syntax to the actual character,\nyou can specify the [DecodeOptions.interpretNumericEntities] option as well:\n\n```dart\nexpect(\n  QS.decode(\n    'a=%26%239786%3B',\n    const DecodeOptions(\n      charset: latin1,\n      interpretNumericEntities: true,\n    ),\n  ),\n  equals({'a': '☺'}),\n);\n```\n\nIt also works when the charset has been detected in [DecodeOptions.charsetSentinel] mode.\n\n### Decoding Lists\n\n[decode] can also decode [List]s using a similar `[]` notation:\n\n```dart\nexpect(\n  QS.decode('a[]=b\u0026a[]=c'),\n  equals({\n    'a': ['b', 'c']\n  }),\n);\n```\n\nYou may specify an index as well:\n\n```dart\nexpect(\n  QS.decode('a[1]=c\u0026a[0]=b'),\n  equals({\n    'a': ['b', 'c']\n  }),\n);\n```\n\nNote that the only difference between an index in a [List] and a key in a [Map] is that the value between the brackets\nmust be a number to create a [List]. When creating [List]s with specific indices, [decode] will compact a sparse \n[List] to only the existing values preserving their order:\n\n```dart\nexpect(\n  QS.decode('a[1]=b\u0026a[15]=c'),\n  equals({\n    'a': ['b', 'c']\n  }),\n);\n```\n\nNote that an empty string is also a value, and will be preserved:\n\n```dart\nexpect(\n  QS.decode('a[]=\u0026a[]=b'),\n  equals({\n    'a': ['', 'b']\n  }),\n);\nexpect(\n  QS.decode('a[0]=b\u0026a[1]=\u0026a[2]=c'),\n  equals({\n    'a': ['b', '', 'c']\n  }),\n);\n```\n\n[decode] will also limit specifying indices in a [List] to a maximum index of **20**.\nAny [List] members with an index of greater than **20** will instead be converted to a [Map] with the index as the key.\nThis is needed to handle cases when someone sent, for example, `a[999999999]` and it will take significant time to iterate \nover this huge [List].\n\n```dart\nexpect(\n  QS.decode('a[100]=b'),\n  equals({\n    'a': {'100': 'b'}\n  }),\n);\n```\n\nThis limit can be overridden by passing an [DecodeOptions.listLimit] option:\n\n```dart\nexpect(\n  QS.decode(\n    'a[1]=b',\n    const DecodeOptions(listLimit: 0),\n  ),\n  equals({\n    'a': {'1': 'b'}\n  }),\n);\n```\n\nTo disable List parsing entirely, set [DecodeOptions.parseLists] to `false`.\n\n```dart\nexpect(\n  QS.decode(\n    'a[]=b',\n    const DecodeOptions(parseLists: false),\n  ),\n  equals({\n    'a': {'0': 'b'}\n  }),\n);\n```\n\nIf you mix notations, [decode] will merge the two items into a [Map]:\n\n```dart\nexpect(\n  QS.decode('a[0]=b\u0026a[b]=c'),\n  equals({\n    'a': {'0': 'b', 'b': 'c'}\n  }),\n);\n```\n\nYou can also create [List]s of [Map]s:\n\n```dart\nexpect(\n  QS.decode('a[][b]=c'),\n  equals({\n    'a': [\n      {'b': 'c'}\n    ]\n  }),\n);\n```\n\nSome systems join list items with commas; [decode] can parse this when [DecodeOptions.comma] is set to `true`:\n\n```dart\nexpect(\n  QS.decode(\n    'a=b,c',\n    const DecodeOptions(comma: true),\n  ),\n  equals({\n    'a': ['b', 'c']\n  }),\n);\n```\n\n(_[decode] cannot convert nested [Map]s, such as `'a={b:1},{c:d}'`_)\n\n### Decoding primitive/scalar values (`num`, `bool`, `null`, etc.)\n\nBy default, all values are parsed as [String]s.\n\n```dart\nexpect(\n  QS.decode('a=15\u0026b=true\u0026c=null'),\n  equals({\n    'a': '15',\n    'b': 'true',\n    'c': 'null',\n  }),\n);\n```\n\n### Encoding\n\n```dart\nString encode(\n  Object? object, [\n  EncodeOptions options = const EncodeOptions(),\n]);\n```\n\n[encode] will by default URI encode the output. [Map]s are stringified as you would expect:\n\n```dart\nexpect(\n  QS.encode({'a': 'b'}),\n  equals('a=b'),\n);\nexpect(\n  QS.encode({'a': {'b': 'c'}}),\n  equals('a%5Bb%5D=c'),\n);\n```\n\nThis encoding can be disabled by setting the [EncodeOptions.encode] option to `false`:\n\n```dart\nexpect(\n  QS.encode(\n    {\n      'a': {'b': 'c'}\n    },\n    const EncodeOptions(encode: false),\n  ),\n  equals('a[b]=c'),\n);\n```\n\nEncoding can be disabled for keys by setting the [EncodeOptions.encodeValuesOnly] option to `true`:\n\n```dart\nexpect(\n  QS.encode(\n    {\n      'a': 'b',\n      'c': ['d', 'e=f'],\n      'f': [\n        ['g'],\n        ['h']\n      ]\n    },\n    const EncodeOptions(encodeValuesOnly: true),\n  ),\n  equals('a=b\u0026c[0]=d\u0026c[1]=e%3Df\u0026f[0][0]=g\u0026f[1][0]=h'),\n);\n```\n\nYou can supply a custom [Encoder] via [EncodeOptions.encoder]:\n\n```dart\nexpect(\n  QS.encode(\n    {\n      'a': {'b': 'č'}\n    },\n    EncodeOptions(\n      encoder: (\n        str, {\n        Encoding? charset,\n        Format? format,\n      }) =\u003e\n          switch (str) {\n        'č' =\u003e 'c',\n        _ =\u003e str,\n      },\n    ),\n  ),\n  equals('a[b]=c'),\n);\n```\n\n_(Note: [EncodeOptions.encoder] is ignored when [EncodeOptions.encode] is `false`.)_\n\nSimilar to [EncodeOptions.encoder] there is a [DecodeOptions.decoder] option for [decode] to override decoding of \nproperties and values:\n\n```dart\nexpect(\n  QS.decode(\n    'foo=123', \n    DecodeOptions(\n      decoder: (String? str, {Encoding? charset}) =\u003e\n        num.tryParse(str ?? '') ?? str,\n    ),\n  ),\n  equals({'foo': 123}),\n);\n```\n\nExamples beyond this point will be shown as though the output is not URI encoded for clarity.\nPlease note that the return values in these cases *will* be URI encoded during real usage.\n\nWhen [List]s are encoded, they follow the [EncodeOptions.listFormat] option, which defaults to [ListFormat.indices]:\n\n```dart\nexpect(\n  QS.encode(\n    {\n      'a': ['b', 'c', 'd']\n    },\n    const EncodeOptions(encode: false),\n  ),\n  equals('a[0]=b\u0026a[1]=c\u0026a[2]=d'),\n);\n```\n\nYou can override this by setting [EncodeOptions.indices] to `false`, or, more explicitly, the\n[EncodeOptions.listFormat] option to [ListFormat.repeat]:\n\n```dart\nexpect(\n  QS.encode(\n    {\n      'a': ['b', 'c', 'd']\n    },\n    const EncodeOptions(\n      encode: false,\n      indices: false,\n    ),\n  ),\n  equals('a=b\u0026a=c\u0026a=d'),\n);\n```\n\nYou can choose the list encoding style with [EncodeOptions.listFormat]:\n\n```dart\nexpect(\n  QS.encode(\n    {\n      'a': ['b', 'c']\n    },\n    const EncodeOptions(\n      encode: false,\n      listFormat: ListFormat.indices,\n    ),\n  ),\n  equals('a[0]=b\u0026a[1]=c'),\n);\n\nexpect(\n  QS.encode(\n    {\n      'a': ['b', 'c']\n    },\n    const EncodeOptions(\n      encode: false,\n      listFormat: ListFormat.brackets,\n    ),\n  ),\n  equals('a[]=b\u0026a[]=c'),\n);\n\nexpect(\n  QS.encode(\n    {\n      'a': ['b', 'c']\n    },\n    const EncodeOptions(\n      encode: false,\n      listFormat: ListFormat.repeat,\n    ),\n  ),\n  equals('a=b\u0026a=c'),\n);\n\nexpect(\n  QS.encode(\n    {\n      'a': ['b', 'c']\n    },\n    const EncodeOptions(\n      encode: false,\n      listFormat: ListFormat.comma,\n    ),\n  ),\n  equals('a=b,c'),\n);\n```\n\n**Note:** When using [EncodeOptions.listFormat] set to [ListFormat.comma], you can also pass the [EncodeOptions.commaRoundTrip]\noption set to `true` or `false`, to append `[]` on single-item [List]s, so that they can round trip through a parse.\n\nWhen [Map]s are encoded, by default they use bracket notation:\n\n```dart\nexpect(\n  QS.encode(\n    {\n      'a': {\n        'b': {'c': 'd', 'e': 'f'}\n      }\n    },\n    const EncodeOptions(encode: false),\n  ),\n  equals('a[b][c]=d\u0026a[b][e]=f'),\n);\n```\n\nYou may override this to use dot notation by setting the [EncodeOptions.allowDots] option to `true`:\n\n```dart\nexpect(\n  QS.encode(\n    {\n      'a': {\n        'b': {'c': 'd', 'e': 'f'}\n      }\n    },\n    const EncodeOptions(\n      encode: false,\n      allowDots: true,\n    ),\n  ),\n  equals('a.b.c=d\u0026a.b.e=f'),\n);\n```\n\nEncode literal dots in keys with [EncodeOptions.encodeDotInKeys] (use with [EncodeOptions.allowDots]):\n\n```dart\nexpect(\n  QS.encode(\n    {\n      'name.obj': {'first': 'John', 'last': 'Doe'}\n    },\n    const EncodeOptions(\n      allowDots: true,\n      encodeDotInKeys: true,\n    ),\n  ),\n  equals('name%252Eobj.first=John\u0026name%252Eobj.last=Doe'),\n);\n```\n\n**Caveat:** when both [EncodeOptions.encodeValuesOnly] and [EncodeOptions.encodeDotInKeys] are `true`, only dots in keys are encoded; values remain unchanged.\n\nYou may allow empty [List] values by setting the [EncodeOptions.allowEmptyLists] option to `true`:\n\n```dart\nexpect(\n  QS.encode(\n    {\n      'foo': [],\n      'bar': 'baz',\n    },\n    const EncodeOptions(\n      encode: false,\n      allowEmptyLists: true,\n    ),\n  ),\n  equals('foo[]\u0026bar=baz'),\n);\n```\n\nEmpty strings and null values will omit the value, but the equals sign (`=`) remains in place:\n\n```dart\nexpect(\n  QS.encode(\n    {\n      'a': '',\n    },\n  ),\n  equals('a='),\n);\n```\n\nKeys with no values (e.g., empty [Map]s or [List]s) produce no output:\n\n```dart\nexpect(\n  QS.encode(\n    {\n      'a': [],\n    },\n  ),\n  equals(''),\n);\n\nexpect(\n  QS.encode(\n    {\n      'a': {},\n    },\n  ),\n  equals(''),\n);\n\nexpect(\n  QS.encode(\n    {\n      'a': [{}],\n    },\n  ),\n  equals('')\n);\n\nexpect(\n  QS.encode(\n    {\n      'a': {'b': []},\n    },\n  ),\n  equals('')\n);\n\nexpect(\n  QS.encode(\n    {\n      'a': {'b': {}},\n    },\n  ),\n  equals('')\n);\n```\n\nProperties set to [Undefined] are omitted entirely:\n\n```dart\nexpect(\n  QS.encode(\n    {\n      'a': null,\n      'b': const Undefined(),\n    },\n  ),\n  equals('a='),\n);\n```\n\nThe query string may optionally be prepended with a question mark:\n\n```dart\nexpect(\n  QS.encode(\n    {\n      'a': 'b',\n      'c': 'd',\n    },\n    const EncodeOptions(addQueryPrefix: true),\n  ),\n  equals('?a=b\u0026c=d'),\n);\n```\n\nThe delimiter may be overridden as well:\n\n```dart\nexpect(\n  QS.encode(\n    {\n      'a': 'b',\n      'c': 'd',\n    },\n    const EncodeOptions(delimiter: ';'),\n  ),\n  equals('a=b;c=d'),\n);\n```\n\nIf you only want to override the serialization of [DateTime] objects, you can provide a custom [DateSerializer] in the\n[EncodeOptions.serializeDate] option:\n\n```dart\nexpect(\n  QS.encode(\n    {\n      'a': DateTime.fromMillisecondsSinceEpoch(7).toUtc(),\n    },\n    const EncodeOptions(encode: false),\n  ),\n  equals('a=1970-01-01T00:00:00.007Z'),\n);\nexpect(\n  QS.encode(\n    {\n      'a': DateTime.fromMillisecondsSinceEpoch(7).toUtc(),\n    },\n    EncodeOptions(\n      encode: false,\n      serializeDate: (DateTime date) =\u003e\n          date.millisecondsSinceEpoch.toString(),\n    ),\n  ),\n  equals('a=7'),\n);\n```\n\nYou may use the [EncodeOptions.sort] option to affect the order of parameter keys:\n\n```dart\nexpect(\n  QS.encode(\n    {\n      'a': 'c',\n      'z': 'y',\n      'b': 'f',\n    },\n    EncodeOptions(\n      encode: false,\n      sort: (a, b) =\u003e a.compareTo(b),\n    ),\n  ),\n  equals('a=c\u0026b=f\u0026z=y'),\n);\n```\n\nFinally, you can use the [EncodeOptions.filter] option to restrict which keys will be included in the encoded output.\nIf you pass a [Function], it will be called for each key to obtain the replacement value.\nOtherwise, if you pass a [List], it will be used to select properties and [List] indices to be encoded:\n\n```dart\nexpect(\n  QS.encode(\n    {\n      'a': 'b',\n      'c': 'd',\n      'e': {\n        'f': DateTime.fromMillisecondsSinceEpoch(123),\n        'g': [2],\n      },\n    },\n    EncodeOptions(\n      encode: false,\n      filter: (prefix, value) =\u003e switch (prefix) {\n        'b' =\u003e const Undefined(),\n        'e[f]' =\u003e (value as DateTime).millisecondsSinceEpoch,\n        'e[g][0]' =\u003e (value as num) * 2,\n        _ =\u003e value,\n      },\n    ),\n  ),\n  equals('a=b\u0026c=d\u0026e[f]=123\u0026e[g][0]=4'),\n);\n\nexpect(\n  QS.encode(\n    {\n      'a': 'b',\n      'c': 'd',\n      'e': 'f',\n    },\n    const EncodeOptions(\n      encode: false,\n      filter: ['a', 'e'],\n    ),\n  ),\n  equals('a=b\u0026e=f'),\n);\n\nexpect(\n  QS.encode(\n    {\n      'a': ['b', 'c', 'd'],\n      'e': 'f',\n    },\n    const EncodeOptions(\n      encode: false,\n      filter: ['a', 0, 2],\n    ),\n  ),\n  equals('a[0]=b\u0026a[2]=d'),\n);\n```\n\n### Handling of `null` values\n\nBy default, `null` values are treated like empty strings:\n\n```dart\nexpect(\n  QS.encode(\n    {\n      'a': null,\n      'b': '',\n    },\n  ),\n  equals('a=\u0026b='),\n);\n```\n\nDecoding does not distinguish between parameters with and without equal signs.\nBoth are converted to empty strings.\n\n```dart\nexpect(\n  QS.decode('a\u0026b='),\n  equals({\n    'a': '',\n    'b': '',\n  }),\n);\n```\n\nTo distinguish between `null` values and empty [String]s use the [EncodeOptions.strictNullHandling] flag. \nIn the result string the `null` values have no `=` sign:\n\n```dart\nexpect(\n  QS.encode(\n    {\n      'a': null,\n      'b': '',\n    },\n    const EncodeOptions(strictNullHandling: true),\n  ),\n  equals('a\u0026b='),\n);\n```\n\nTo decode values without `=` back to `null` use the [DecodeOptions.strictNullHandling] flag:\n\n```dart\nexpect(\n  QS.decode(\n    'a\u0026b=',\n    const DecodeOptions(strictNullHandling: true),\n  ),\n  equals({\n    'a': null,\n    'b': '',\n  }),\n);\n```\n\nTo completely skip rendering keys with `null` values, use the [EncodeOptions.skipNulls] flag:\n\n```dart\nexpect(\n  QS.encode(\n    {\n      'a': 'b',\n      'c': null,\n    },\n    const EncodeOptions(skipNulls: true),\n  ),\n  equals('a=b'),\n);\n```\n\nIf you're communicating with legacy systems, you can switch to [latin1] using the [EncodeOptions.charset] option:\n\n```dart\nexpect(\n  QS.encode(\n    {\n      'æ': 'æ',\n    },\n    const EncodeOptions(charset: latin1),\n  ),\n  equals('%E6=%E6'),\n);\n```\n\nCharacters that don't exist in [latin1] will be converted to numeric entities, similar to what browsers do:\n\n```dart\nexpect(\n  QS.encode(\n    {\n      'a': '☺',\n    },\n    const EncodeOptions(charset: latin1),\n  ),\n  equals('a=%26%239786%3B'),\n);\n```\n\nYou can use the [EncodeOptions.charsetSentinel] option to announce the character by including an `utf8=✓` parameter with\nthe proper encoding of the checkmark, similar to what Ruby on Rails and others do when submitting forms.\n\n```dart\nexpect(\n  QS.encode(\n    {\n      'a': '☺',\n    },\n    const EncodeOptions(charsetSentinel: true),\n  ),\n  equals('utf8=%E2%9C%93\u0026a=%E2%98%BA'),\n);\nexpect(\n  QS.encode(\n    {\n      'a': 'æ',\n    },\n    const EncodeOptions(\n      charset: latin1,\n      charsetSentinel: true,\n    ),\n  ),\n  equals('utf8=%26%2310003%3B\u0026a=%E6'),\n);\n```\n\n### Dealing with special character sets\n\nBy default, the encoding and decoding of characters is done in [utf8], and [latin1] support is also built in via \nthe [EncodeOptions.charset] and [DecodeOptions.charset] parameter, respectively.\n\nIf you wish to encode query strings to a different character set (i.e.\n[Shift JIS](https://en.wikipedia.org/wiki/Shift_JIS)) you can use the [euc](https://pub.dev/packages/euc) package\n\n```dart\nexpect(\n  QS.encode(\n    {\n      'a': 'こんにちは！',\n    },\n    EncodeOptions(\n      encoder: (str, {Encoding? charset, Format? format}) {\n        if ((str as String?)?.isNotEmpty ?? false) {\n          final Uint8List buf = Uint8List.fromList(\n            ShiftJIS().encode(str!),\n          );\n          final List\u003cString\u003e result = [\n            for (int i = 0; i \u003c buf.length; ++i) buf[i].toRadixString(16)\n          ];\n          return '%${result.join('%')}';\n        }\n        return '';\n      },\n    ),\n  ),\n  equals('%61=%82%b1%82%f1%82%c9%82%bf%82%cd%81%49'),\n);\n```\n\nThis also works for decoding of query strings:\n\n```dart\nexpect(\n  QS.decode(\n    '%61=%82%b1%82%f1%82%c9%82%bf%82%cd%81%49',\n    DecodeOptions(\n      decoder: (str, {Encoding? charset}) {\n        if (str == null) {\n          return null;\n        }\n\n        final RegExp reg = RegExp(r'%([0-9A-F]{2})', caseSensitive: false);\n        final List\u003cint\u003e result = [];\n        Match? parts;\n        while ((parts = reg.firstMatch(str!)) != null \u0026\u0026 parts != null) {\n          result.add(int.parse(parts.group(1)!, radix: 16));\n          str = str.substring(parts.end);\n        }\n        return ShiftJIS().decode(\n          Uint8List.fromList(result),\n        );\n      },\n    ),\n  ),\n  equals({\n    'a': 'こんにちは！',\n  }),\n);\n```\n\n### RFC 3986 and RFC 1738 space encoding\n\nThe default [EncodeOptions.format] is [Format.rfc3986] which encodes `' '` to `%20` which is backward compatible.\nYou can also set the [EncodeOptions.format] to [Format.rfc1738] which encodes `' '` to `+`.\n\n```dart\nexpect(\n  QS.encode(\n    {\n      'a': 'b c',\n    },\n  ),\n  equals('a=b%20c'),\n);\n\nexpect(\n  QS.encode(\n    {\n      'a': 'b c',\n    },\n    const EncodeOptions(format: Format.rfc3986),\n  ),\n  equals('a=b%20c'),\n);\n\nexpect(\n  QS.encode(\n    {\n      'a': 'b c',\n    },\n    const EncodeOptions(format: Format.rfc1738),\n  ),\n  equals('a=b+c'),\n);\n```\n\n---\n\n## Other ports\n\n| Port                       | Repository                                                  | Package                                                                                                                                                                                       |\n|----------------------------|-------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|\n| Python                     | [techouse/qs_codec](https://github.com/techouse/qs_codec)   | [![PyPI](https://img.shields.io/pypi/v/qs-codec?logo=python\u0026label=PyPI)](https://pypi.org/project/qs-codec/)                                                                                  |\n| Kotlin / JVM + Android AAR | [techouse/qs-kotlin](https://github.com/techouse/qs-kotlin) | [![Maven Central](https://img.shields.io/maven-central/v/io.github.techouse/qs-kotlin?logo=kotlin\u0026label=Maven%20Central)](https://central.sonatype.com/artifact/io.github.techouse/qs-kotlin) |\n| Swift / Objective-C        | [techouse/qs-swift](https://github.com/techouse/qs-swift)   | [![SPM](https://img.shields.io/github/v/release/techouse/qs-swift?logo=swift\u0026label=SwiftPM)](https://swiftpackageindex.com/techouse/qs-swift)                                                 |\n| .NET / C#                  | [techouse/qs-net](https://github.com/techouse/qs-net)       | [![NuGet](https://img.shields.io/nuget/v/QsNet?logo=dotnet\u0026label=NuGet)](https://www.nuget.org/packages/QsNet)                                                                                |\n| Node.js (original)         | [ljharb/qs](https://github.com/ljharb/qs)                   | [![npm](https://img.shields.io/npm/v/qs?logo=javascript\u0026label=npm)](https://www.npmjs.com/package/qs)                                                                                         |\n\n---\n\nSpecial thanks to the authors of [qs](https://www.npmjs.com/package/qs) for JavaScript:\n- [Jordan Harband](https://github.com/ljharb)\n- [TJ Holowaychuk](https://github.com/visionmedia/node-querystring)\n","funding_links":["https://github.com/sponsors/techouse","https://paypal.me/ktusar"],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftechouse%2Fqs","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftechouse%2Fqs","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftechouse%2Fqs/lists"}