QsNet.Refit
1.4.4
dotnet add package QsNet.Refit --version 1.4.4
NuGet\Install-Package QsNet.Refit -Version 1.4.4
<PackageReference Include="QsNet.Refit" Version="1.4.4" />
<PackageVersion Include="QsNet.Refit" Version="1.4.4" />
<PackageReference Include="QsNet.Refit" />
paket add QsNet.Refit --version 1.4.4
#r "nuget: QsNet.Refit, 1.4.4"
#:package QsNet.Refit@1.4.4
#addin nuget:?package=QsNet.Refit&version=1.4.4
#tool nuget:?package=QsNet.Refit&version=1.4.4
QsNet

A query string encoding and decoding library for C#/.NET.
Ported from qs for JavaScript.
Highlights
- Nested dictionaries and lists:
foo[bar][baz]=qux⇄{ "foo": { "bar": { "baz": "qux" } } } - Multiple list formats (indices, brackets, repeat, comma)
- Dot-notation support (
a.b=c) and"."-encoding toggles - UTF-8 and Latin1 charsets, plus optional charset sentinel (
utf8=✓) - Custom encoders/decoders, key sorting, filtering, and strict null handling
- Supports
DateTimeserialization via a pluggable serializer - Extensive tests (xUnit + FluentAssertions), performance-minded implementation
Installation
NuGet Package Manager
Install-Package QsNet
.NET CLI
dotnet add package QsNet
Package Reference
<PackageReference Include="QsNet" Version="<version>"/>
Optional integrations
Core QsNet stays framework-agnostic. Install an adapter package only when you
need integration with a specific HTTP or URL library.
| Package | Use when | Install |
|---|---|---|
QsNet.AspNetCore |
You want ASP.NET Core helpers for parsing or appending qs-style query strings. | dotnet add package QsNet.AspNetCore |
QsNet.Flurl |
You build URLs with Flurl and want qs-style nested query parameters. | dotnet add package QsNet.Flurl |
QsNet.Refit |
You use Refit and need to pass a QsNet-generated query string through a request interface. | dotnet add package QsNet.Refit |
QsNet.RestSharp |
You use RestSharp and want to add QsNet-generated nested query parameters to a RestRequest. |
dotnet add package QsNet.RestSharp |
QsNet.Refit does not depend on Refit at runtime. Install Refit separately in
the application or test project that declares the Refit API interface.
Requirements
- Target frameworks (TFMs):
net10.0,netstandard2.0 - Supported runtimes (via the target frameworks above):
| Runtime | Version | CI Coverage | Status |
|---|---|---|---|
| .NET | 10 (LTS) | Full CI | Supported |
| .NET | 9 (STS) | Consumer smoke test | Supported |
| .NET | 8 (LTS) | Consumer smoke test | Supported |
| .NET | 7 | Consumer smoke test | Supported |
| .NET | 6 (LTS) | Consumer smoke test | Supported |
| .NET | 5 | Optional smoke (non-blocking) | EOL |
| .NET Core | 3.1 | Compile-only smoke | EOL |
| .NET Framework | 4.6.1+ | Smoke test (4.6.1, 4.8.1) | Supported |
- Platforms: Windows, Linux, macOS (cross-platform, no native dependencies)
Quick start
using QsNet;
// Decode
Dictionary<string, object?> obj = Qs.Decode("foo[bar]=baz&foo[list][]=a&foo[list][]=b");
// -> { "foo": { "bar": "baz", "list": ["a", "b"] } }
// Encode
string qs = Qs.Encode(new Dictionary<string, object?>
{
["foo"] = new Dictionary<string, object?> { ["bar"] = "baz" }
});
// -> "foo%5Bbar%5D=baz"
ASP.NET Core helpers
using QsNet.AspNetCore;
string url = "/api/search".AddQueryString(new Dictionary<string, object?>
{
["filter"] = new Dictionary<string, object?> { ["name"] = "Alice" }
});
// -> "/api/search?filter%5Bname%5D=Alice"
Dictionary<string, object?> query = httpContext.Request.ToQueryMap();
QsNet.AspNetCore appends the already encoded QsNet output directly, preserving
fragments and bracket notation without re-encoding through ASP.NET Core
QueryHelpers.
Flurl helpers
using Flurl;
using QsNet.Flurl;
var url = "https://api.example.com"
.AppendPathSegment("products")
.AppendQsQueryParams(new
{
filter = new { name = "Alice" },
tags = new[] { "one", "two" },
});
// -> "https://api.example.com/products?filter%5Bname%5D=Alice&tags%5B0%5D=one&tags%5B1%5D=two"
QsNet.Flurl writes QsNet's already encoded output through Flurl's Url.Query
instead of Flurl's normal query-parameter APIs, avoiding double-encoding of
qs-style bracket notation.
Refit helpers
using QsNet.Models;
using QsNet.Refit;
using Refit;
public interface IUsersApi
{
[Get("/users")]
[QueryUriFormat(UriFormat.Unescaped)]
Task<List<User>> GetUsers([Query] QsQuery query);
}
await api.GetUsers(QsQuery.From(
new
{
Roles = new[]
{
new { Name = "Developer", Level = 1 },
},
},
new EncodeOptions { AllowDots = true }));
// -> "/users?Roles%5B0%5D.Name=Developer&Roles%5B0%5D.Level=1"
QsNet.Refit formalizes a wrapper workaround for complex nested query strings.
It does not change Refit's native [Query] object serializer for DTOs.
RestSharp helpers
using QsNet.RestSharp;
using RestSharp;
var request = new RestRequest("products")
.AddQsQueryParameters(new
{
filter = new
{
where = new
{
name = "John",
age = new { gte = 30 },
},
},
tags = new[] { "a", "b" },
});
// -> "/products?filter%5Bwhere%5D%5Bname%5D=John&filter%5Bwhere%5D%5Bage%5D%5Bgte%5D=30&tags%5B0%5D=a&tags%5B1%5D=b"
QsNet.RestSharp adds already encoded QsNet query pairs to RestSharp with
query encoding disabled, avoiding double-encoding of qs-style bracket notation.
Usage
Simple
// Decode
Dictionary<string, object?> decoded = Qs.Decode("a=c");
// => { "a": "c" }
// Encode
string encoded = Qs.Encode(new Dictionary<string, object?> { ["a"] = "c" });
// => "a=c"
Decoding
URI queries
Use DecodeQsQuery to decode the escaped query component of an absolute or
relative Uri without pre-decoding percent escapes or including the fragment:
var uri = new Uri(
"https://example.com/search?filter%5Bwhere%5D%5Bname%5D=John%20Doe&tag=a&tag=b&flag&empty=#results"
);
var query = uri.DecodeQsQuery(
new DecodeOptions { StrictNullHandling = true }
);
// => { "filter": { "where": { "name": "John Doe" } }, "tag": ["a", "b"], "flag": null, "empty": "" }
Here, encoded brackets reach QsNet unchanged, the fragment is ignored, the two
tag values use the configured duplicate handling, flag decodes as null,
and empty= decodes as an empty string. An absent or empty query returns an
empty dictionary.
The helper is intentionally read-only. For a new URI with no existing query,
construct the URI explicitly from default Qs.Encode(...) output:
var values = new Dictionary<string, object?> { ["page"] = "2" };
var encoded = Qs.Encode(values);
var uri = new Uri($"https://api.example.com/products?{encoded}");
AddQueryPrefix is false by default. If you enable it, interpolate the
encoded output without adding another literal ?, or remove the prefix first.
Do not use this pattern to merge with an arbitrary existing query: decoding and
re-encoding can change duplicate ordering, name-only keys, delimiters, list
notation, and percent spelling. Encode = false or a custom encoder can also
produce text unsuitable for Uri construction.
Nested dictionaries
Qs.Decode("foo[bar]=baz");
// => { "foo": { "bar": "baz" } }
Qs.Decode("a%5Bb%5D=c");
// => { "a": { "b": "c" } }
Qs.Decode("foo[bar][baz]=foobarbaz");
// => { "foo": { "bar": { "baz": "foobarbaz" } } }
Depth (default: 5)
Beyond the configured depth, remaining bracket content is kept as literal text:
Qs.Decode("a[b][c][d][e][f][g][h][i]=j");
// => { "a": { "b": { "c": { "d": { "e": { "f": { "[g][h][i]": "j" } } } } } } }
Override depth:
Qs.Decode("a[b][c][d][e][f][g][h][i]=j", new DecodeOptions { Depth = 1 });
// => { "a": { "b": { "[c][d][e][f][g][h][i]": "j" } } }
Parameter limit
Qs.Decode("a=b&c=d", new DecodeOptions { ParameterLimit = 1 });
// => { "a": "b" }
Ignore leading ?
Qs.Decode("?a=b&c=d", new DecodeOptions { IgnoreQueryPrefix = true });
// => { "a": "b", "c": "d" }
Custom delimiter (string or regex)
Qs.Decode("a=b;c=d", new DecodeOptions { Delimiter = new StringDelimiter(";") });
// => { "a": "b", "c": "d" }
Qs.Decode("a=b;c=d", new DecodeOptions { Delimiter = new RegexDelimiter("[;,]") });
// => { "a": "b", "c": "d" }
Dot-notation and "decode dots in keys"
Qs.Decode("a.b=c", new DecodeOptions { AllowDots = true });
// => { "a": { "b": "c" } }
Qs.Decode(
"name%252Eobj.first=John&name%252Eobj.last=Doe",
new DecodeOptions { DecodeDotInKeys = true }
);
// => { "name.obj": { "first": "John", "last": "Doe" } }
Empty lists
Qs.Decode("foo[]&bar=baz", new DecodeOptions { AllowEmptyLists = true });
// => { "foo": [], "bar": "baz" }
Duplicates
Qs.Decode("foo=bar&foo=baz");
// => { "foo": ["bar", "baz"] }
Qs.Decode("foo=bar&foo=baz", new DecodeOptions { Duplicates = Duplicates.Combine });
// => same as above
Qs.Decode("foo=bar&foo=baz", new DecodeOptions { Duplicates = Duplicates.First });
// => { "foo": "bar" }
Qs.Decode("foo=bar&foo=baz", new DecodeOptions { Duplicates = Duplicates.Last });
// => { "foo": "baz" }
Bracket-array keys always combine, even when plain duplicate keys are configured to keep only the first or last value:
Qs.Decode("foo[]=bar&foo[]=baz", new DecodeOptions { Duplicates = Duplicates.First });
// => { "foo": ["bar", "baz"] }
Strict merge
Object/primitive merge conflicts wrap into a list by default:
Qs.Decode("a[b]=c&a=d");
// => { "a": [{ "b": "c" }, "d"] }
Set StrictMerge to false to preserve the legacy QsNet behavior for
object-then-primitive conflicts:
Qs.Decode("a[b]=c&a=d", new DecodeOptions { StrictMerge = false });
// => { "a": { "b": "c", "d": true } }
Charset and sentinel
// Latin1
Qs.Decode("a=%A7", new DecodeOptions { Charset = Encoding.Latin1 });
// => { "a": "§" }
// Sentinels
Qs.Decode("utf8=%E2%9C%93&a=%C3%B8", new DecodeOptions { Charset = Encoding.Latin1, CharsetSentinel = true });
// => { "a": "ø" }
Qs.Decode("utf8=%26%2310003%3B&a=%F8", new DecodeOptions { Charset = Encoding.UTF8, CharsetSentinel = true });
// => { "a": "ø" }
Interpret numeric entities (Ӓ)
Qs.Decode(
"a=%26%239786%3B",
new DecodeOptions { Charset = Encoding.Latin1, InterpretNumericEntities = true }
);
// => { "a": "☺" }
Lists
Qs.Decode("a[]=b&a[]=c");
// => { "a": ["b", "c"] }
Qs.Decode("a[1]=c&a[0]=b");
// => { "a": ["b", "c"] }
Qs.Decode("a[1]=b&a[15]=c");
// => { "a": ["b", "c"] }
Qs.Decode("a[]=&a[]=b");
// => { "a": ["", "b"] }
ListLimit is the maximum element count for lists. Explicit numeric indices are
list entries only when index < ListLimit; an index at or above the limit
becomes a dictionary entry by default, or throws when ThrowOnLimitExceeded is
true. Implicit list growth, comma lists, and duplicate-combine paths use the same
element count before overflow conversion or exception.
Large indices convert to a dictionary by default:
Qs.Decode("a[100]=b");
// => { "a": { 100: "b" } }
Disable list parsing:
Qs.Decode("a[]=b", new DecodeOptions { ParseLists = false });
// => { "a": { 0: "b" } }
Mixing notations merges into a dictionary:
Qs.Decode("a[0]=b&a[b]=c");
// => { "a": { 0: "b", "b": "c" } }
Comma-separated values:
Qs.Decode("a=b,c", new DecodeOptions { Comma = true });
// => { "a": ["b", "c"] }
Primitive/scalar values
All values decode as strings by default:
Qs.Decode("a=15&b=true&c=null");
// => { "a": "15", "b": "true", "c": "null" }
Encoding
Basics
Qs.Encode(new Dictionary<string, object?> { ["a"] = "b" });
// => "a=b"
Qs.Encode(new Dictionary<string, object?>
{
["a"] = new Dictionary<string, object?> { ["b"] = "c" }
});
// => "a%5Bb%5D=c"
Disable URI encoding for readability:
Qs.Encode(
new Dictionary<string, object?>
{
["a"] = new Dictionary<string, object?> { ["b"] = "c" }
},
new EncodeOptions { Encode = false }
);
// => "a[b]=c"
Values-only encoding:
Qs.Encode(
new Dictionary<string, object?>
{
["a"] = "b",
["c"] = new List<object?> { "d", "e=f" },
["f"] = new List<object?>
{
new List<object?> { "g" },
new List<object?> { "h" },
},
},
new EncodeOptions { EncodeValuesOnly = true }
);
// => "a=b&c[0]=d&c[1]=e%3Df&f[0][0]=g&f[1][0]=h"
Custom encoder:
Qs.Encode(
new Dictionary<string, object?>
{
["a"] = new Dictionary<string, object?> { ["b"] = "č" },
},
new EncodeOptions
{
Encoder = (str, _, _) => str?.ToString() == "č" ? "c" : str?.ToString() ?? "",
}
);
// => "a[b]=c"
List formats
var data = new Dictionary<string, object?> { ["a"] = new List<object?> { "b", "c" } };
var options = new EncodeOptions { Encode = false };
// default (indices)
Qs.Encode(data, options.CopyWith(listFormat: ListFormat.Indices));
// => "a[0]=b&a[1]=c"
// brackets
Qs.Encode(data, options.CopyWith(listFormat: ListFormat.Brackets));
// => "a[]=b&a[]=c"
// repeat
Qs.Encode(data, options.CopyWith(listFormat: ListFormat.Repeat));
// => "a=b&a=c"
// comma
Qs.Encode(data, options.CopyWith(listFormat: ListFormat.Comma));
// => "a=b,c"
Note: When ListFormat.Comma is selected, you can set EncodeOptions.CommaRoundTrip to true or false to append
[] on single-item lists so they round-trip through decoding. Set EncodeOptions.CommaCompactNulls to true alongside
the comma format when you'd like to drop null entries instead of keeping empty slots (for example,
["one", null, "two"] becomes one,two).
Nested dictionaries
Qs.Encode(
new Dictionary<string, object?>
{
["a"] = new Dictionary<string, object?>
{
["b"] = new Dictionary<string, object?> { ["c"] = "d", ["e"] = "f" },
},
},
new EncodeOptions { Encode = false }
);
// => "a[b][c]=d&a[b][e]=f"
Dot notation:
Qs.Encode(
new Dictionary<string, object?>
{
["a"] = new Dictionary<string, object?>
{
["b"] = new Dictionary<string, object?> { ["c"] = "d", ["e"] = "f" },
},
},
new EncodeOptions { Encode = false, AllowDots = true }
);
// => "a.b.c=d&a.b.e=f"
Encode dots in keys:
Qs.Encode(
new Dictionary<string, object?>
{
["name.obj"] = new Dictionary<string, object?>
{
["first"] = "John",
["last"] = "Doe",
},
},
new EncodeOptions { AllowDots = true, EncodeDotInKeys = true }
);
// => "name%252Eobj.first=John&name%252Eobj.last=Doe"
Allow empty lists:
Qs.Encode(
new Dictionary<string, object?> { ["foo"] = new List<object?>(), ["bar"] = "baz" },
new EncodeOptions { Encode = false, AllowEmptyLists = true }
);
// => "foo[]&bar=baz"
Empty strings and nulls:
Qs.Encode(new Dictionary<string, object?> { ["a"] = "" });
// => "a="
Return empty string for empty containers:
Qs.Encode(new Dictionary<string, object?> { ["a"] = new List<object?>() }); // => ""
Qs.Encode(new Dictionary<string, object?> { ["a"] = new Dictionary<string, object?>() }); // => ""
Qs.Encode(new Dictionary<string, object?> { ["a"] = new List<object?> { new Dictionary<string, object?>() } }); // => ""
Qs.Encode(new Dictionary<string, object?> { ["a"] = new Dictionary<string, object?> { ["b"] = new List<object?>() } }); // => ""
Qs.Encode(new Dictionary<string, object?> { ["a"] = new Dictionary<string, object?> { ["b"] = new Dictionary<string, object?>() } }); // => ""
Omit Undefined:
Qs.Encode(new Dictionary<string, object?> { ["a"] = null, ["b"] = Undefined.Create() });
// => "a="
Add query prefix:
Qs.Encode(
new Dictionary<string, object?> { ["a"] = "b", ["c"] = "d" },
new EncodeOptions { AddQueryPrefix = true }
);
// => "?a=b&c=d"
Custom delimiter:
Qs.Encode(
new Dictionary<string, object?> { ["a"] = "b", ["c"] = "d" },
new EncodeOptions { Delimiter = ";" }
);
// => "a=b;c=d"
Dates
By default, DateTime is serialized using ToString() in ISO 8601 format.
var date = new DateTime(1970, 1, 1, 0, 0, 0, 7, DateTimeKind.Utc);
Qs.Encode(
new Dictionary<string, object?> { ["a"] = date },
new EncodeOptions { Encode = false }
);
// => "a=1970-01-01T00:00:00.0070000Z"
Qs.Encode(
new Dictionary<string, object?> { ["a"] = date },
new EncodeOptions
{
Encode = false,
DateSerializer = d => ((DateTimeOffset)d).ToUnixTimeMilliseconds().ToString(),
}
);
// => "a=7"
Sorting & filtering
// Sort keys
Qs.Encode(
new Dictionary<string, object?>
{
["a"] = "c",
["z"] = "y",
["b"] = "f",
},
new EncodeOptions
{
Encode = false,
Sort = (a, b) => string.Compare(a?.ToString(), b?.ToString(), StringComparison.Ordinal),
}
);
// => "a=c&b=f&z=y"
// Filter by function (drop/transform values)
var epochStart = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
var testDate = epochStart.AddMilliseconds(123);
Qs.Encode(
new Dictionary<string, object?>
{
["a"] = "b",
["c"] = "d",
["e"] = new Dictionary<string, object?>
{
["f"] = testDate,
["g"] = new List<object?> { 2 },
},
},
new EncodeOptions
{
Encode = false,
Filter = new FunctionFilter(
(prefix, value) =>
prefix switch
{
"b" => Undefined.Create(),
"e[f]" => (long)((DateTime)value! - epochStart).TotalMilliseconds,
"e[g][0]" => Convert.ToInt32(value) * 2,
_ => value,
}
),
}
);
// => "a=b&c=d&e[f]=123&e[g][0]=4"
// Filter by explicit list of keys/indices
Qs.Encode(
new Dictionary<string, object?>
{
["a"] = "b",
["c"] = "d",
["e"] = "f",
},
new EncodeOptions
{
Encode = false,
Filter = new IterableFilter(new List<object> { "a", "e" }),
}
);
// => "a=b&e=f"
Qs.Encode(
new Dictionary<string, object?>
{
["a"] = new List<object?> { "b", "c", "d" },
["e"] = "f",
},
new EncodeOptions
{
Encode = false,
Filter = new IterableFilter(new List<object> { "a", 0, 2 }),
}
);
// => "a[0]=b&a[2]=d"
Null handling
// Treat null values like empty strings by default
Qs.Encode(new Dictionary<string, object?> { ["a"] = null, ["b"] = "" });
// => "a=&b="
// Cannot distinguish between parameters with and without equal signs
Qs.Decode("a&b=");
// => { "a": "", "b": "" }
// Distinguish between null values and empty strings using strict null handling
Qs.Encode(
new Dictionary<string, object?> { ["a"] = null, ["b"] = "" },
new EncodeOptions { StrictNullHandling = true }
);
// => "a&b="
// Decode values without equals back to null using strict null handling
Qs.Decode("a&b=", new DecodeOptions { StrictNullHandling = true });
// => { "a": null, "b": "" }
// Completely skip rendering keys with null values using skip nulls
Qs.Encode(
new Dictionary<string, object?> { ["a"] = "b", ["c"] = null },
new EncodeOptions { SkipNulls = true }
);
// => "a=b"
Charset handling
Note (Latin-1 on older TFMs): Some frameworks (e.g., netstandard2.0) don’t expose
Encoding.Latin1directly. UseEncoding.GetEncoding("iso-8859-1"). On .NET Core / netstandard you may also need to register the code pages provider:using System.Text; Encoding.RegisterProvider(CodePagesEncodingProvider.Instance); var latin1 = Encoding.GetEncoding("iso-8859-1");
// Encode using Latin1 charset
Qs.Encode(
new Dictionary<string, object?> { ["æ"] = "æ" },
new EncodeOptions { Charset = Encoding.Latin1 }
);
// => "%E6=%E6"
// Convert characters that don't exist in Latin1 to numeric entities
Qs.Encode(
new Dictionary<string, object?> { ["a"] = "☺" },
new EncodeOptions { Charset = Encoding.Latin1 }
);
// => "a=%26%239786%3B"
// Announce charset using charset sentinel option with UTF-8
Qs.Encode(
new Dictionary<string, object?> { ["a"] = "☺" },
new EncodeOptions { CharsetSentinel = true }
);
// => "utf8=%E2%9C%93&a=%E2%98%BA"
// Announce charset using charset sentinel option with Latin1
Qs.Encode(
new Dictionary<string, object?> { ["a"] = "æ" },
new EncodeOptions { Charset = Encoding.Latin1, CharsetSentinel = true }
);
// => "utf8=%26%2310003%3B&a=%E6"
RFC 3986 vs RFC 1738 space encoding
Qs.Encode(new Dictionary<string, object?> { ["a"] = "b c" });
// => "a=b%20c" (RFC 3986 default)
Qs.Encode(new Dictionary<string, object?> { ["a"] = "b c" }, new EncodeOptions { Format = Format.Rfc3986 });
// => "a=b%20c"
Qs.Encode(new Dictionary<string, object?> { ["a"] = "b c" }, new EncodeOptions { Format = Format.Rfc1738 });
// => "a=b+c"
Design notes
- Performance: The implementation mirrors qs semantics but is optimized for C#/.NET. Deep parsing, list compaction, and cycle-safe compaction are implemented iteratively where it matters.
- Safety: Defaults (depth, parameterLimit) help mitigate abuse in user-supplied inputs; you can loosen them when you fully trust the source.
- Interop: Exposes knobs similar to qs (filters, sorters, custom encoders/decoders) to make migrations straightforward.
Other ports
| Port | Repository | Package |
|---|---|---|
| Dart | techouse/qs | |
| Python | techouse/qs_codec | |
| Kotlin / JVM + Android AAR | techouse/qs-kotlin | |
| Swift / Objective-C | techouse/qs-swift | |
| Rust | techouse/qs_rust | |
| Node.js (original) | ljharb/qs |
Special thanks to the authors of qs for JavaScript:
License
BSD 3-Clause © techouse
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net5.0 was computed. net5.0-windows was computed. net6.0 was computed. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 was computed. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 was computed. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
| .NET Core | netcoreapp2.0 was computed. netcoreapp2.1 was computed. netcoreapp2.2 was computed. netcoreapp3.0 was computed. netcoreapp3.1 was computed. |
| .NET Standard | netstandard2.0 is compatible. netstandard2.1 was computed. |
| .NET Framework | net461 was computed. net462 was computed. net463 was computed. net47 was computed. net471 was computed. net472 was computed. net48 was computed. net481 was computed. |
| MonoAndroid | monoandroid was computed. |
| MonoMac | monomac was computed. |
| MonoTouch | monotouch was computed. |
| Tizen | tizen40 was computed. tizen60 was computed. |
| Xamarin.iOS | xamarinios was computed. |
| Xamarin.Mac | xamarinmac was computed. |
| Xamarin.TVOS | xamarintvos was computed. |
| Xamarin.WatchOS | xamarinwatchos was computed. |
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
See CHANGELOG: https://github.com/techouse/qs-net/releases