{"id":30196239,"url":"https://github.com/techouse/qs-net","last_synced_at":"2026-02-15T16:17:59.517Z","repository":{"id":308740600,"uuid":"1033869927","full_name":"techouse/qs-net","owner":"techouse","description":"A query string encoding and decoding library for C#/.NET. Ported from qs for JavaScript.","archived":false,"fork":false,"pushed_at":"2026-02-14T13:44:07.000Z","size":7498,"stargazers_count":3,"open_issues_count":1,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2026-02-14T16:59:32.888Z","etag":null,"topics":["csharp","dotnet","qs","query-encoding","query-string","url-parsing","url-query"],"latest_commit_sha":null,"homepage":"https://techouse.github.io/qs-net/","language":"C#","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":"2025-08-07T13:25:33.000Z","updated_at":"2026-02-08T14:12:26.000Z","dependencies_parsed_at":"2025-08-07T17:18:28.531Z","dependency_job_id":"bdd4e7e6-1723-416f-b808-b1b44e7a7f69","html_url":"https://github.com/techouse/qs-net","commit_stats":null,"previous_names":["techouse/qs-net"],"tags_count":15,"template":false,"template_full_name":null,"purl":"pkg:github/techouse/qs-net","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/techouse%2Fqs-net","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/techouse%2Fqs-net/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/techouse%2Fqs-net/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/techouse%2Fqs-net/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/techouse","download_url":"https://codeload.github.com/techouse/qs-net/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/techouse%2Fqs-net/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29481924,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-15T11:35:25.641Z","status":"ssl_error","status_checked_at":"2026-02-15T11:34:57.128Z","response_time":118,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"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":["csharp","dotnet","qs","query-encoding","query-string","url-parsing","url-query"],"created_at":"2025-08-13T05:17:13.550Z","updated_at":"2026-02-15T16:17:59.511Z","avatar_url":"https://github.com/techouse.png","language":"C#","readme":"# QsNet\n\n![QsNet](https://raw.githubusercontent.com/techouse/qs-net/refs/heads/main/logo_cropped.png?raw=true)\n\nA query string encoding and decoding library for C#/.NET.\n\nPorted from [qs](https://www.npmjs.com/package/qs) for JavaScript.\n\n[![Targets](https://img.shields.io/badge/dynamic/xml?url=https%3A%2F%2Fraw.githubusercontent.com%2Ftechouse%2Fqs-net%2Fmain%2FQsNet%2FQsNet.csproj\u0026query=string(//TargetFrameworks%7C//TargetFramework)\u0026label=targets\u0026logo=dotnet\u0026color=512BD4\u0026labelColor=222222\u0026logoColor=white\u0026style=flat-square)](https://www.nuget.org/packages/QsNet)\n[![DocFX Docs](https://img.shields.io/badge/DocFX-docs-512BD4?logo=dotnet\u0026logoColor=white\u0026labelColor=222222\u0026style=flat-square)](https://techouse.github.io/qs-net/)\n[![NuGet Version](https://img.shields.io/nuget/v/QsNet)](https://www.nuget.org/packages/QsNet)\n[![NuGet Downloads](https://img.shields.io/nuget/dt/QsNet)](https://www.nuget.org/packages/QsNet)\n[![Test](https://github.com/techouse/qs-net/actions/workflows/test.yml/badge.svg)](https://github.com/techouse/qs-net/actions/workflows/test.yml)\n[![codecov](https://codecov.io/gh/techouse/qs-net/graph/badge.svg?token=pmMJRph0Qm)](https://codecov.io/gh/techouse/qs-net)\n[![Codacy Badge](https://app.codacy.com/project/badge/Grade/32d093a7035f461ab3829f3d8f11b824)](https://app.codacy.com/gh/techouse/qs-net/dashboard?utm_source=gh\u0026utm_medium=referral\u0026utm_content=\u0026utm_campaign=Badge_grade)\n[![GitHub](https://img.shields.io/github/license/techouse/qs-net)](LICENSE)\n[![GitHub Repo stars](https://img.shields.io/github/stars/techouse/qs-net)](https://github.com/techouse/qs-net/stargazers)\n\n---\n\n## Highlights\n\n- Nested dictionaries and lists: `foo[bar][baz]=qux` ⇄ `{ \"foo\": { \"bar\": { \"baz\": \"qux\" } } }`\n- Multiple list formats (indices, brackets, repeat, comma)\n- Dot-notation support (`a.b=c`) and `\".\"`-encoding toggles\n- UTF-8 and Latin1 charsets, plus optional charset sentinel (`utf8=✓`)\n- Custom encoders/decoders, key sorting, filtering, and strict null handling\n- Supports `DateTime` serialization via a pluggable serializer\n- Extensive tests (xUnit + FluentAssertions), performance-minded implementation\n\n---\n\n## Installation\n\n### NuGet Package Manager\n\n```\nInstall-Package QsNet\n```\n\n### .NET CLI\n\n```bash\ndotnet add package QsNet\n```\n\n### Package Reference\n\n```xml\n\n\u003cPackageReference Include=\"QsNet\" Version=\"\u003cversion\u003e\"/\u003e\n```\n\n---\n\n## Requirements\n\n- **Target frameworks (TFMs):** `net8.0`, `netstandard2.0`\n- **Supported runtimes (via the target frameworks above):**\n\n| Runtime        | Version  | CI Coverage                   | Status    |\n|----------------|----------|-------------------------------|-----------|\n| .NET           | 10 (LTS) | Full CI                       | Supported |\n| .NET           | 9 (STS)  | Full CI                       | Supported |\n| .NET           | 8 (LTS)  | Full CI                       | Supported |\n| .NET           | 7        | Consumer smoke test           | Supported |\n| .NET           | 6 (LTS)  | Consumer smoke test           | Supported |\n| .NET           | 5        | Optional smoke (non-blocking) | EOL       |\n| .NET Core      | 3.1      | Compile-only smoke            | EOL       |\n| .NET Framework | 4.6.1+   | Smoke test (4.6.1, 4.8.1)     | Supported |\n\n- **Platforms:** Windows, Linux, macOS (cross-platform, no native dependencies)\n\n---\n\n## Quick start\n\n```csharp\nusing QsNet;\n\n// Decode\nDictionary\u003cstring, object?\u003e obj = Qs.Decode(\"foo[bar]=baz\u0026foo[list][]=a\u0026foo[list][]=b\");\n// -\u003e { \"foo\": { \"bar\": \"baz\", \"list\": [\"a\", \"b\"] } }\n\n// Encode\nstring qs = Qs.Encode(new Dictionary\u003cstring, object?\u003e \n{ \n    [\"foo\"] = new Dictionary\u003cstring, object?\u003e { [\"bar\"] = \"baz\" } \n});\n// -\u003e \"foo%5Bbar%5D=baz\"\n```\n\n---\n\n## Usage\n\n### Simple\n\n```csharp\n// Decode\nDictionary\u003cstring, object?\u003e decoded = Qs.Decode(\"a=c\");\n// =\u003e { \"a\": \"c\" }\n\n// Encode\nstring encoded = Qs.Encode(new Dictionary\u003cstring, object?\u003e { [\"a\"] = \"c\" });\n// =\u003e \"a=c\"\n```\n\n---\n\n## Decoding\n\n### Nested dictionaries\n\n```csharp\nQs.Decode(\"foo[bar]=baz\");\n// =\u003e { \"foo\": { \"bar\": \"baz\" } }\n\nQs.Decode(\"a%5Bb%5D=c\");\n// =\u003e { \"a\": { \"b\": \"c\" } }\n\nQs.Decode(\"foo[bar][baz]=foobarbaz\");\n// =\u003e { \"foo\": { \"bar\": { \"baz\": \"foobarbaz\" } } }\n```\n\n### Depth (default: 5)\n\nBeyond the configured depth, remaining bracket content is kept as literal text:\n\n```csharp\nQs.Decode(\"a[b][c][d][e][f][g][h][i]=j\");\n// =\u003e { \"a\": { \"b\": { \"c\": { \"d\": { \"e\": { \"f\": { \"[g][h][i]\": \"j\" } } } } } } }\n```\n\nOverride depth:\n\n```csharp\nQs.Decode(\"a[b][c][d][e][f][g][h][i]=j\", new DecodeOptions { Depth = 1 });\n// =\u003e { \"a\": { \"b\": { \"[c][d][e][f][g][h][i]\": \"j\" } } }\n```\n\n### Parameter limit\n\n```csharp\nQs.Decode(\"a=b\u0026c=d\", new DecodeOptions { ParameterLimit = 1 });\n// =\u003e { \"a\": \"b\" }\n```\n\n### Ignore leading `?`\n\n```csharp\nQs.Decode(\"?a=b\u0026c=d\", new DecodeOptions { IgnoreQueryPrefix = true });\n// =\u003e { \"a\": \"b\", \"c\": \"d\" }\n```\n\n### Custom delimiter (string or regex)\n\n```csharp\nQs.Decode(\"a=b;c=d\", new DecodeOptions { Delimiter = new StringDelimiter(\";\") });\n// =\u003e { \"a\": \"b\", \"c\": \"d\" }\n\nQs.Decode(\"a=b;c=d\", new DecodeOptions { Delimiter = new RegexDelimiter(\"[;,]\") });\n// =\u003e { \"a\": \"b\", \"c\": \"d\" }\n```\n\n### Dot-notation and \"decode dots in keys\"\n\n```csharp\nQs.Decode(\"a.b=c\", new DecodeOptions { AllowDots = true });\n// =\u003e { \"a\": { \"b\": \"c\" } }\n\nQs.Decode(\n    \"name%252Eobj.first=John\u0026name%252Eobj.last=Doe\",\n    new DecodeOptions { DecodeDotInKeys = true }\n);\n// =\u003e { \"name.obj\": { \"first\": \"John\", \"last\": \"Doe\" } }\n```\n\n### Empty lists\n\n```csharp\nQs.Decode(\"foo[]\u0026bar=baz\", new DecodeOptions { AllowEmptyLists = true });\n// =\u003e { \"foo\": [], \"bar\": \"baz\" }\n```\n\n### Duplicates\n\n```csharp\nQs.Decode(\"foo=bar\u0026foo=baz\");\n// =\u003e { \"foo\": [\"bar\", \"baz\"] }\n\nQs.Decode(\"foo=bar\u0026foo=baz\", new DecodeOptions { Duplicates = Duplicates.Combine });\n// =\u003e same as above\n\nQs.Decode(\"foo=bar\u0026foo=baz\", new DecodeOptions { Duplicates = Duplicates.First });\n// =\u003e { \"foo\": \"bar\" }\n\nQs.Decode(\"foo=bar\u0026foo=baz\", new DecodeOptions { Duplicates = Duplicates.Last });\n// =\u003e { \"foo\": \"baz\" }\n```\n\n### Charset and sentinel\n\n```csharp\n// Latin1\nQs.Decode(\"a=%A7\", new DecodeOptions { Charset = Encoding.Latin1 });\n// =\u003e { \"a\": \"§\" }\n\n// Sentinels\nQs.Decode(\"utf8=%E2%9C%93\u0026a=%C3%B8\", new DecodeOptions { Charset = Encoding.Latin1, CharsetSentinel = true });\n// =\u003e { \"a\": \"ø\" }\n\nQs.Decode(\"utf8=%26%2310003%3B\u0026a=%F8\", new DecodeOptions { Charset = Encoding.UTF8, CharsetSentinel = true });\n// =\u003e { \"a\": \"ø\" }\n```\n\n### Interpret numeric entities (`\u0026#1234;`)\n\n```csharp\nQs.Decode(\n    \"a=%26%239786%3B\",\n    new DecodeOptions { Charset = Encoding.Latin1, InterpretNumericEntities = true }\n);\n// =\u003e { \"a\": \"☺\" }\n```\n\n### Lists\n\n```csharp\nQs.Decode(\"a[]=b\u0026a[]=c\");\n// =\u003e { \"a\": [\"b\", \"c\"] }\n\nQs.Decode(\"a[1]=c\u0026a[0]=b\");\n// =\u003e { \"a\": [\"b\", \"c\"] }\n\nQs.Decode(\"a[1]=b\u0026a[15]=c\");\n// =\u003e { \"a\": [\"b\", \"c\"] }\n\nQs.Decode(\"a[]=\u0026a[]=b\");\n// =\u003e { \"a\": [\"\", \"b\"] }\n```\n\nLarge indices convert to a dictionary by default:\n\n```csharp\nQs.Decode(\"a[100]=b\");\n// =\u003e { \"a\": { 100: \"b\" } }\n```\n\nDisable list parsing:\n\n```csharp\nQs.Decode(\"a[]=b\", new DecodeOptions { ParseLists = false });\n// =\u003e { \"a\": { 0: \"b\" } }\n```\n\nMixing notations merges into a dictionary:\n\n```csharp\nQs.Decode(\"a[0]=b\u0026a[b]=c\");\n// =\u003e { \"a\": { 0: \"b\", \"b\": \"c\" } }\n```\n\nComma-separated values:\n\n```csharp\nQs.Decode(\"a=b,c\", new DecodeOptions { Comma = true });\n// =\u003e { \"a\": [\"b\", \"c\"] }\n```\n\n### Primitive/scalar values\n\nAll values decode as strings by default:\n\n```csharp\nQs.Decode(\"a=15\u0026b=true\u0026c=null\");\n// =\u003e { \"a\": \"15\", \"b\": \"true\", \"c\": \"null\" }\n```\n\n---\n\n## Encoding\n\n### Basics\n\n```csharp\nQs.Encode(new Dictionary\u003cstring, object?\u003e { [\"a\"] = \"b\" });\n// =\u003e \"a=b\"\n\nQs.Encode(new Dictionary\u003cstring, object?\u003e \n{ \n    [\"a\"] = new Dictionary\u003cstring, object?\u003e { [\"b\"] = \"c\" } \n});\n// =\u003e \"a%5Bb%5D=c\"\n```\n\nDisable URI encoding for readability:\n\n```csharp\nQs.Encode(\n    new Dictionary\u003cstring, object?\u003e \n    { \n        [\"a\"] = new Dictionary\u003cstring, object?\u003e { [\"b\"] = \"c\" } \n    },\n    new EncodeOptions { Encode = false }\n);\n// =\u003e \"a[b]=c\"\n```\n\nValues-only encoding:\n\n```csharp\nQs.Encode(\n    new Dictionary\u003cstring, object?\u003e\n    {\n        [\"a\"] = \"b\",\n        [\"c\"] = new List\u003cobject?\u003e { \"d\", \"e=f\" },\n        [\"f\"] = new List\u003cobject?\u003e\n        {\n            new List\u003cobject?\u003e { \"g\" },\n            new List\u003cobject?\u003e { \"h\" },\n        },\n    },\n    new EncodeOptions { EncodeValuesOnly = true }\n);\n// =\u003e \"a=b\u0026c[0]=d\u0026c[1]=e%3Df\u0026f[0][0]=g\u0026f[1][0]=h\"\n```\n\nCustom encoder:\n\n```csharp\nQs.Encode(\n    new Dictionary\u003cstring, object?\u003e\n    {\n        [\"a\"] = new Dictionary\u003cstring, object?\u003e { [\"b\"] = \"č\" },\n    },\n    new EncodeOptions\n    {\n        Encoder = (str, _, _) =\u003e str?.ToString() == \"č\" ? \"c\" : str?.ToString() ?? \"\",\n    }\n);\n// =\u003e \"a[b]=c\"\n```\n\n### List formats\n\n```csharp\nvar data = new Dictionary\u003cstring, object?\u003e { [\"a\"] = new List\u003cobject?\u003e { \"b\", \"c\" } };\nvar options = new EncodeOptions { Encode = false };\n\n// default (indices)\nQs.Encode(data, options.CopyWith(listFormat: ListFormat.Indices));\n// =\u003e \"a[0]=b\u0026a[1]=c\"\n\n// brackets\nQs.Encode(data, options.CopyWith(listFormat: ListFormat.Brackets));\n// =\u003e \"a[]=b\u0026a[]=c\"\n\n// repeat\nQs.Encode(data, options.CopyWith(listFormat: ListFormat.Repeat));\n// =\u003e \"a=b\u0026a=c\"\n\n// comma\nQs.Encode(data, options.CopyWith(listFormat: ListFormat.Comma));\n// =\u003e \"a=b,c\"\n```\n\n**Note:** When `ListFormat.Comma` is selected, you can set `EncodeOptions.CommaRoundTrip` to `true` or `false` to append\n`[]` on single-item lists so they round-trip through decoding. Set `EncodeOptions.CommaCompactNulls` to `true` alongside\nthe comma format when you'd like to drop `null` entries instead of keeping empty slots (for example,\n`[\"one\", null, \"two\"]` becomes `one,two`).\n\n### Nested dictionaries\n\n```csharp\nQs.Encode(\n    new Dictionary\u003cstring, object?\u003e\n    {\n        [\"a\"] = new Dictionary\u003cstring, object?\u003e\n        {\n            [\"b\"] = new Dictionary\u003cstring, object?\u003e { [\"c\"] = \"d\", [\"e\"] = \"f\" },\n        },\n    },\n    new EncodeOptions { Encode = false }\n);\n// =\u003e \"a[b][c]=d\u0026a[b][e]=f\"\n```\n\nDot notation:\n\n```csharp\nQs.Encode(\n    new Dictionary\u003cstring, object?\u003e\n    {\n        [\"a\"] = new Dictionary\u003cstring, object?\u003e\n        {\n            [\"b\"] = new Dictionary\u003cstring, object?\u003e { [\"c\"] = \"d\", [\"e\"] = \"f\" },\n        },\n    },\n    new EncodeOptions { Encode = false, AllowDots = true }\n);\n// =\u003e \"a.b.c=d\u0026a.b.e=f\"\n```\n\nEncode dots in keys:\n\n```csharp\nQs.Encode(\n    new Dictionary\u003cstring, object?\u003e\n    {\n        [\"name.obj\"] = new Dictionary\u003cstring, object?\u003e\n        {\n            [\"first\"] = \"John\",\n            [\"last\"] = \"Doe\",\n        },\n    },\n    new EncodeOptions { AllowDots = true, EncodeDotInKeys = true }\n);\n// =\u003e \"name%252Eobj.first=John\u0026name%252Eobj.last=Doe\"\n```\n\nAllow empty lists:\n\n```csharp\nQs.Encode(\n    new Dictionary\u003cstring, object?\u003e { [\"foo\"] = new List\u003cobject?\u003e(), [\"bar\"] = \"baz\" },\n    new EncodeOptions { Encode = false, AllowEmptyLists = true }\n);\n// =\u003e \"foo[]\u0026bar=baz\"\n```\n\nEmpty strings and nulls:\n\n```csharp\nQs.Encode(new Dictionary\u003cstring, object?\u003e { [\"a\"] = \"\" });\n// =\u003e \"a=\"\n```\n\nReturn empty string for empty containers:\n\n```csharp\nQs.Encode(new Dictionary\u003cstring, object?\u003e { [\"a\"] = new List\u003cobject?\u003e() });        // =\u003e \"\"\nQs.Encode(new Dictionary\u003cstring, object?\u003e { [\"a\"] = new Dictionary\u003cstring, object?\u003e() });    // =\u003e \"\"\nQs.Encode(new Dictionary\u003cstring, object?\u003e { [\"a\"] = new List\u003cobject?\u003e { new Dictionary\u003cstring, object?\u003e() } }); // =\u003e \"\"\nQs.Encode(new Dictionary\u003cstring, object?\u003e { [\"a\"] = new Dictionary\u003cstring, object?\u003e { [\"b\"] = new List\u003cobject?\u003e() } }); // =\u003e \"\"\nQs.Encode(new Dictionary\u003cstring, object?\u003e { [\"a\"] = new Dictionary\u003cstring, object?\u003e { [\"b\"] = new Dictionary\u003cstring, object?\u003e() } }); // =\u003e \"\"\n```\n\nOmit `Undefined`:\n\n```csharp\nQs.Encode(new Dictionary\u003cstring, object?\u003e { [\"a\"] = null, [\"b\"] = Undefined.Create() });\n// =\u003e \"a=\"\n```\n\nAdd query prefix:\n\n```csharp\nQs.Encode(\n    new Dictionary\u003cstring, object?\u003e { [\"a\"] = \"b\", [\"c\"] = \"d\" },\n    new EncodeOptions { AddQueryPrefix = true }\n);\n// =\u003e \"?a=b\u0026c=d\"\n```\n\nCustom delimiter:\n\n```csharp\nQs.Encode(\n    new Dictionary\u003cstring, object?\u003e { [\"a\"] = \"b\", [\"c\"] = \"d\" },\n    new EncodeOptions { Delimiter = \";\" }\n);\n// =\u003e \"a=b;c=d\"\n```\n\n### Dates\n\nBy default, `DateTime` is serialized using `ToString()` in ISO 8601 format.\n\n```csharp\nvar date = new DateTime(1970, 1, 1, 0, 0, 0, 7, DateTimeKind.Utc);\n\nQs.Encode(\n    new Dictionary\u003cstring, object?\u003e { [\"a\"] = date },\n    new EncodeOptions { Encode = false }\n);\n// =\u003e \"a=1970-01-01T00:00:00.0070000Z\"\n\nQs.Encode(\n    new Dictionary\u003cstring, object?\u003e { [\"a\"] = date },\n    new EncodeOptions\n    {\n        Encode = false,\n        DateSerializer = d =\u003e ((DateTimeOffset)d).ToUnixTimeMilliseconds().ToString(),\n    }\n);\n// =\u003e \"a=7\"\n```\n\n### Sorting \u0026 filtering\n\n```csharp\n// Sort keys\nQs.Encode(\n    new Dictionary\u003cstring, object?\u003e\n    {\n        [\"a\"] = \"c\",\n        [\"z\"] = \"y\",\n        [\"b\"] = \"f\",\n    },\n    new EncodeOptions\n    {\n        Encode = false,\n        Sort = (a, b) =\u003e string.Compare(a?.ToString(), b?.ToString(), StringComparison.Ordinal),\n    }\n);\n// =\u003e \"a=c\u0026b=f\u0026z=y\"\n\n// Filter by function (drop/transform values)\nvar epochStart = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);\nvar testDate = epochStart.AddMilliseconds(123);\n\nQs.Encode(\n    new Dictionary\u003cstring, object?\u003e\n    {\n        [\"a\"] = \"b\",\n        [\"c\"] = \"d\",\n        [\"e\"] = new Dictionary\u003cstring, object?\u003e\n        {\n            [\"f\"] = testDate,\n            [\"g\"] = new List\u003cobject?\u003e { 2 },\n        },\n    },\n    new EncodeOptions\n    {\n        Encode = false,\n        Filter = new FunctionFilter(\n            (prefix, value) =\u003e\n                prefix switch\n                {\n                    \"b\" =\u003e Undefined.Create(),\n                    \"e[f]\" =\u003e (long)((DateTime)value! - epochStart).TotalMilliseconds,\n                    \"e[g][0]\" =\u003e Convert.ToInt32(value) * 2,\n                    _ =\u003e value,\n                }\n        ),\n    }\n);\n// =\u003e \"a=b\u0026c=d\u0026e[f]=123\u0026e[g][0]=4\"\n\n// Filter by explicit list of keys/indices\nQs.Encode(\n    new Dictionary\u003cstring, object?\u003e\n    {\n        [\"a\"] = \"b\",\n        [\"c\"] = \"d\",\n        [\"e\"] = \"f\",\n    },\n    new EncodeOptions\n    {\n        Encode = false,\n        Filter = new IterableFilter(new List\u003cobject\u003e { \"a\", \"e\" }),\n    }\n);\n// =\u003e \"a=b\u0026e=f\"\n\nQs.Encode(\n    new Dictionary\u003cstring, object?\u003e\n    {\n        [\"a\"] = new List\u003cobject?\u003e { \"b\", \"c\", \"d\" },\n        [\"e\"] = \"f\",\n    },\n    new EncodeOptions\n    {\n        Encode = false,\n        Filter = new IterableFilter(new List\u003cobject\u003e { \"a\", 0, 2 }),\n    }\n);\n// =\u003e \"a[0]=b\u0026a[2]=d\"\n```\n\n### Null handling\n\n```csharp\n// Treat null values like empty strings by default\nQs.Encode(new Dictionary\u003cstring, object?\u003e { [\"a\"] = null, [\"b\"] = \"\" });\n// =\u003e \"a=\u0026b=\"\n\n// Cannot distinguish between parameters with and without equal signs\nQs.Decode(\"a\u0026b=\");\n// =\u003e { \"a\": \"\", \"b\": \"\" }\n\n// Distinguish between null values and empty strings using strict null handling\nQs.Encode(\n    new Dictionary\u003cstring, object?\u003e { [\"a\"] = null, [\"b\"] = \"\" },\n    new EncodeOptions { StrictNullHandling = true }\n);\n// =\u003e \"a\u0026b=\"\n\n// Decode values without equals back to null using strict null handling\nQs.Decode(\"a\u0026b=\", new DecodeOptions { StrictNullHandling = true });\n// =\u003e { \"a\": null, \"b\": \"\" }\n\n// Completely skip rendering keys with null values using skip nulls\nQs.Encode(\n    new Dictionary\u003cstring, object?\u003e { [\"a\"] = \"b\", [\"c\"] = null },\n    new EncodeOptions { SkipNulls = true }\n);\n// =\u003e \"a=b\"\n```\n\n### Charset handling\n\n\u003e **Note (Latin-1 on older TFMs):** Some frameworks (e.g., netstandard2.0) don’t expose `Encoding.Latin1` directly. Use\n`Encoding.GetEncoding(\"iso-8859-1\")`. On .NET Core / netstandard you may also need to register the code pages provider:\n\u003e\n\u003e ```csharp\n\u003e using System.Text;\n\u003e\n\u003e Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);\n\u003e var latin1 = Encoding.GetEncoding(\"iso-8859-1\");\n\u003e ```\n\n```csharp\n// Encode using Latin1 charset\nQs.Encode(\n    new Dictionary\u003cstring, object?\u003e { [\"æ\"] = \"æ\" },\n    new EncodeOptions { Charset = Encoding.Latin1 }\n);\n// =\u003e \"%E6=%E6\"\n\n// Convert characters that don't exist in Latin1 to numeric entities\nQs.Encode(\n    new Dictionary\u003cstring, object?\u003e { [\"a\"] = \"☺\" },\n    new EncodeOptions { Charset = Encoding.Latin1 }\n);\n// =\u003e \"a=%26%239786%3B\"\n\n// Announce charset using charset sentinel option with UTF-8\nQs.Encode(\n    new Dictionary\u003cstring, object?\u003e { [\"a\"] = \"☺\" },\n    new EncodeOptions { CharsetSentinel = true }\n);\n// =\u003e \"utf8=%E2%9C%93\u0026a=%E2%98%BA\"\n\n// Announce charset using charset sentinel option with Latin1\nQs.Encode(\n    new Dictionary\u003cstring, object?\u003e { [\"a\"] = \"æ\" },\n    new EncodeOptions { Charset = Encoding.Latin1, CharsetSentinel = true }\n);\n// =\u003e \"utf8=%26%2310003%3B\u0026a=%E6\"\n```\n\n### RFC 3986 vs RFC 1738 space encoding\n\n```csharp\nQs.Encode(new Dictionary\u003cstring, object?\u003e { [\"a\"] = \"b c\" });\n// =\u003e \"a=b%20c\"   (RFC 3986 default)\n\nQs.Encode(new Dictionary\u003cstring, object?\u003e { [\"a\"] = \"b c\" }, new EncodeOptions { Format = Format.Rfc3986 });\n// =\u003e \"a=b%20c\"\n\nQs.Encode(new Dictionary\u003cstring, object?\u003e { [\"a\"] = \"b c\" }, new EncodeOptions { Format = Format.Rfc1738 });\n// =\u003e \"a=b+c\"\n```\n\n---\n\n## Design notes\n\n- **Performance:** The implementation mirrors qs semantics but is optimized for C#/.NET. Deep parsing, list compaction,\n  and cycle-safe compaction are implemented iteratively where it matters.\n- **Safety:** Defaults (depth, parameterLimit) help mitigate abuse in user-supplied inputs; you can loosen them when you\n  fully trust the source.\n- **Interop:** Exposes knobs similar to qs (filters, sorters, custom encoders/decoders) to make migrations\n  straightforward.\n\n---\n\n## Other ports\n\n| Port                       | Repository                                                  | Package                                                                                                                                                                                       |\n|----------------------------|-------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|\n| Dart                       | [techouse/qs](https://github.com/techouse/qs)               | [![pub.dev](https://img.shields.io/pub/v/qs_dart?logo=dart\u0026label=pub.dev)](https://pub.dev/packages/qs_dart)                                                                                  |\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| 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\n- [Jordan Harband](https://github.com/ljharb)\n- [TJ Holowaychuk](https://github.com/visionmedia/node-querystring)\n\n---\n\n## License\n\nBSD 3-Clause © [techouse](https://github.com/techouse)\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-net","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftechouse%2Fqs-net","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftechouse%2Fqs-net/lists"}