DalSoft.RestClient 5.0 is the biggest release of our C# Rest Client in years, and it has two headlines: serialization is now powered by System.Text.Json, and a performance pass that gets typed requests to near-native HttpClient numbers.
It's a breaking release - but for most code the migration is one line, and we'll show you exactly what changed and why.
System.Text.Json by default
Since the very first release RestClient has used the brilliant Json.NET - it served us well for a decade. But System.Text.Json is where the .NET ecosystem lives now, and from 5.0 it's our default serializer.
We deliberately made it behave the way a System.Text.Json user would expect - stock behaviour, not a Json.NET emulation:
[JsonPropertyName]attributes are honoured- Property matching is case sensitive
- Failed typed casts throw
System.Text.Json.JsonException
The one place we deviate from stock is when reading - real world systems are less than perfect and send us less than perfect JSON, so trailing commas and comments are accepted by default. A reasonable compromise over strict parsing.
Want to customise serialization? Pass your own JsonSerializerOptions:
var config = new Config()
.SetJsonSerializerOptions(new JsonSerializerOptions(JsonSerializerDefaults.Web));
dynamic client = new RestClient("https://api.github.com", config);
Json.NET isn't going anywhere
If your models rely on the legacy Json.NET behaviour - [JsonProperty] attributes, JsonSerializerSettings, case insensitive matching, lenient date parsing - opt back in with one line and you get the 4.x behaviour back exactly:
var config = new Config().UseNewtonsoftJson(); // optionally takes your JsonSerializerSettings
That's the whole migration for most codebases. We didn't want any implicit magic here - if you want Json.NET you ask for it, otherwise you get System.Text.Json.
The breaking changes
Config.JsonSerializerSettingsandSetJsonSerializerSettingshave been removed - useUseNewtonsoftJson(jsonSerializerSettings)instead.[JsonProperty]attributes are no longer honoured by default - use[JsonPropertyName]or opt in to Json.NET.- Property matching is case sensitive by default - watch out for models that only worked because Json.NET matched case insensitively. This one bit us in our own test suite:
public class User
{
// jsonplaceholder returns "username" - Json.NET happily matched this case insensitively,
// System.Text.Json needs the name mapped
[JsonPropertyName("username")]
public string Username { get; set; }
}
- Failed typed casts now throw
System.Text.Json.JsonExceptionrather than the Json.NET exception types.
Everything else - the dynamic API, the pipeline, the fluent chaining - works exactly as it always has.
The performance pass
Inspired by nikouu's excellent HttpClientBenchmarking repo (well worth a read if you care about HttpClient performance), we added a BenchmarkDotNet project to the repo and went hunting. What we found and fixed:
- UTF-8 all the way through - request bodies are serialized straight to UTF-8 bytes (no intermediate UTF-16 string), and response bodies are read as UTF-8 bytes with the string only decoded if you ask for it.
- The JSON DOM is now lazy - casting a response to your model deserializes directly from the UTF-8 body and never builds a JSON DOM at all. The DOM is only built the first time you use dynamic access.
- Arrays are wrapped lazily -
result.items[0]on a 10,000 element response used to eagerly wrap all 10,000 elements, now you only pay for what you touch. - A pile of allocation trims - cached statics, no more LINQ closures in the request hot path, reflection caching, and URL building that no longer re-splits the same string three times per request.
Before and after, 10,000 item JSON payload, in process (no network), .NET 8:
| Scenario | 4.x | 5.0 |
|---|---|---|
Typed response List<User> | 22.1 ms / 9.87 MB | 13.5 ms / 5.10 MB |
Dynamic access result[0].id | 8.86 ms / 6.59 MB | 5.28 ms / 5.46 MB |
POST List<User> body | 5.83 ms / 3.40 MB | 3.69 ms / 1.13 MB |
Measured against using HttpClient and System.Text.Json by hand, POST request bodies and typed responses now allocate about the same (1.0x) - down from 3.0x and 3.5x respectively in 4.x.
How does it compare to other REST clients?
We also benchmarked a real GET request to the GitHub API deserialized to a typed model, against native HttpClient and the other popular .NET REST clients:
| Client | Median | Allocated |
|---|---|---|
| HttpClient + GetFromJsonAsync | 51.01 ms | 11.38 KB |
| DalSoft.RestClient 5.0 | 41.38 ms | 23.24 KB |
| RestSharp 114.0 | 45.45 ms | 135.74 KB |
| Flurl.Http 4.0.2 | 19.64 ms | 19.79 KB |
| Refit 15.0 | 39.55 ms | 17.80 KB |
| RestClient.Net 7.2.1 | 51.07 ms | 15.27 KB |
An honest word about that table - time over a real network is dominated by latency, so every client lands within noise of native HttpClient. Don't read the time column as a ranking (including where a client appears faster than native - that's statistical noise). The allocations column is what shows the overhead each library adds per request, and that's where the story is: DalSoft.RestClient stays close to native and the lightweight clients while giving you a full dynamic API, and allocates about 6x less than RestSharp.
The benchmarks live in the repo - dotnet run -c Release in the DalSoft.RestClient.Benchmarks project to reproduce them.
net8.0 target
RestClient now multi-targets netstandard2.0 and net8.0. The netstandard2.0 build keeps supporting everything it always has, while the net8.0 build uses the in-box System.Text.Json and drops the legacy package dependencies entirely - if you're on a modern runtime your dependency graph just got smaller.
DalSoft.RestClient.Testing 5.0
DalSoft.RestClient.Testing - our package for testing REST APIs with the ASP.NET Core in-memory TestServer and WebApplicationFactory - has a matching 5.0 release: upgraded to RestClient 5.0 and retargeted to net8.0. The Verify and CreateRestClient APIs are unchanged.
Get it
> dotnet add package DalSoft.RestClient
The full breaking change list and migration notes are in the readme. If you hit anything we missed, raise an issue - and if RestClient is useful to you or your company, please consider becoming a sponsor ❤️