Serialization
IRestSerializer
All serialization goes through IRestSerializer:
public interface IRestSerializer
{
string ContentType { get; }
[RequiresDynamicCode("...")]
[RequiresUnreferencedCode("...")]
ValueTask<T?> DeserializeAsync<T>(Stream stream, CancellationToken ct = default);
[RequiresDynamicCode("...")]
[RequiresUnreferencedCode("...")]
ValueTask SerializeAsync<T>(Stream stream, T value, CancellationToken ct = default);
}
The ContentType property controls both the Content-Type header on requests and the Accept header.
Built-in serializers
System.Text.Json
dotnet add package ZeroAlloc.Rest.SystemTextJson
services.AddIUserApi(options =>
{
options.BaseAddress = new Uri("https://api.example.com");
options.UseSerializer<SystemTextJsonSerializer>();
});
Uses JsonSerializerDefaults.Web (camelCase, case-insensitive). Content-Type: application/json.
MemoryPack
dotnet add package ZeroAlloc.Rest.MemoryPack
options.UseSerializer<MemoryPackSerializer>();
Content-Type: application/x-memorypack. Both endpoints must understand MemoryPack encoding.
MessagePack
dotnet add package ZeroAlloc.Rest.MessagePack
options.UseSerializer<MessagePackSerializer>();
Content-Type: application/x-msgpack.
Custom serializer
Implement IRestSerializer:
public sealed class MySerializer : IRestSerializer
{
public string ContentType => "application/json";
[RequiresDynamicCode("Serialization may require dynamic code.")]
[RequiresUnreferencedCode("Serialization may require unreferenced code.")]
public async ValueTask<T?> DeserializeAsync<T>(Stream stream, CancellationToken ct = default)
{
return await JsonSerializer.DeserializeAsync<T>(stream, cancellationToken: ct);
}
[RequiresDynamicCode("Serialization may require dynamic code.")]
[RequiresUnreferencedCode("Serialization may require unreferenced code.")]
public async ValueTask SerializeAsync<T>(Stream stream, T value, CancellationToken ct = default)
{
await JsonSerializer.SerializeAsync(stream, value, cancellationToken: ct);
}
}
Register it:
options.UseSerializer<MySerializer>();
Per-method serializer override
When one endpoint speaks a different protocol, annotate that specific method:
[ZeroAllocRestClient]
public interface IUploadApi
{
[Post("/upload")]
[Serializer(typeof(MemoryPackSerializer))]
Task UploadAsync([Body] byte[] data, CancellationToken ct = default);
[Get("/status")]
Task<StatusDto> GetStatusAsync(CancellationToken ct = default); // uses default serializer
}
The generator injects MemoryPackSerializer as a constructor parameter and uses it only for UploadAsync. The DI emitter also registers TryAddSingleton<MemoryPackSerializer> automatically.