Serialization

RestClient.Net gives you full control over how requests are serialized and responses are deserialized.

Deserializers

Deserializers convert HttpResponseMessage to your model types:

Func<HttpResponseMessage, CancellationToken, Task<TSuccess>> deserializeSuccess
Func<HttpResponseMessage, CancellationToken, Task<TError>> deserializeError

JSON Deserialization

Using System.Text.Json:

var result = await httpClient.GetAsync(
    url: "https://api.example.com/users/1".ToAbsoluteUrl(),
    deserializeSuccess: async (response, ct) =>
        await response.Content.ReadFromJsonAsync<User>(ct)
        ?? throw new InvalidOperationException("Null response"),
    deserializeError: async (response, ct) =>
        await response.Content.ReadFromJsonAsync<ApiError>(ct)
        ?? new ApiError("Unknown error")
);

Reusable Deserializers

Create a static class with reusable deserializer methods:

public static class Deserializers
{
    public static async Task<T> Json<T>(HttpResponseMessage response, CancellationToken ct)
        where T : class =>
        await response.Content.ReadFromJsonAsync<T>(ct)
        ?? throw new InvalidOperationException($"Failed to deserialize {typeof(T).Name}");

    public static async Task<ApiError> Error(HttpResponseMessage response, CancellationToken ct) =>
        await response.Content.ReadFromJsonAsync<ApiError>(ct)
        ?? new ApiError("Unknown error");
}

// Usage
var result = await httpClient.GetAsync(
    url: "https://api.example.com/users/1".ToAbsoluteUrl(),
    deserializeSuccess: Deserializers.Json<User>,
    deserializeError: Deserializers.Error
);

Custom JSON Options

Configure JsonSerializerOptions for custom serialization behavior:

public static class Deserializers
{
    private static readonly JsonSerializerOptions Options = new()
    {
        PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
        PropertyNameCaseInsensitive = true,
        DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull
    };

    public static async Task<T> Json<T>(HttpResponseMessage response, CancellationToken ct)
        where T : class =>
        await response.Content.ReadFromJsonAsync<T>(Options, ct)
        ?? throw new InvalidOperationException($"Failed to deserialize {typeof(T).Name}");
}

Serializers

Serializers convert your request body to HttpContent. They're used with POST, PUT, and PATCH requests.

Func<TBody, HttpContent> serializeRequest

JSON Serialization

var result = await httpClient.PostAsync(
    url: "https://api.example.com/users".ToAbsoluteUrl(),
    body: new CreateUserRequest("John", "john@example.com"),
    serializeRequest: body => JsonContent.Create(body),
    deserializeSuccess: Deserializers.Json<User>,
    deserializeError: Deserializers.Error
);

Custom Content Types

Form URL Encoded

Using FormUrlEncodedContent:

serializeRequest: body => new FormUrlEncodedContent(new Dictionary<string, string>
{
    ["name"] = body.Name,
    ["email"] = body.Email
})

Multipart Form Data

Using MultipartFormDataContent for file uploads:

serializeRequest: body =>
{
    var content = new MultipartFormDataContent();
    content.Add(new StringContent(body.Name), "name");
    content.Add(new ByteArrayContent(body.FileBytes), "file", body.FileName);
    return content;
}

XML

Using XmlSerializer:

serializeRequest: body =>
{
    var serializer = new XmlSerializer(typeof(TBody));
    using var writer = new StringWriter();
    serializer.Serialize(writer, body);
    return new StringContent(writer.ToString(), Encoding.UTF8, "application/xml");
}

Stream Response

Using Stream for large files:

var result = await httpClient.GetAsync(
    url: "https://api.example.com/files/large".ToAbsoluteUrl(),
    deserializeSuccess: async (response, ct) => await response.Content.ReadAsStreamAsync(ct),
    deserializeError: Deserializers.Error
);

See Also