Features
Incremental source generation
OpenApiWeaver leverages the Roslyn incremental generator pipeline for fast, cached rebuilds. Only the documents that have changed are re-processed, making it efficient even in large solutions with multiple OpenAPI files.
JSON & YAML support
Reads OpenAPI 3.0-3.2 documents in the following formats:
.json- OpenAPI 3.x JSON.yaml/.yml- OpenAPI 3.x YAML
Tag-based sub-clients
Operations are grouped by their OpenAPI tags and exposed as properties on the root client. Each tag becomes a separate sub-client class:
var client = new PetstoreClient(accessToken: "token");
// "Pets" tag -> client.Pets property
var pet = await client.Pets.GetAsync(petId: 1);
// "Users" tag -> client.Users property
var user = await client.Users.GetAsync(userId: "me");Method names are derived from operationId when available, with an Async suffix appended automatically.
When a tag has a description, it is emitted as XML documentation on the generated tag property and sub-client class.
Typed request / response models
Generates sealed classes for object schemas and maps them to strongly typed method parameters and return values:
- Object schemas ->
sealed classwith[JsonPropertyName]attributes - Enums ->
readonly record structwith a dedicatedJsonConverter(string enums), or standardenum(integer enums) — see Schema Type Mapping - Arrays ->
IReadOnlyList<T> - Dictionaries (
additionalProperties/patternProperties) ->IReadOnlyDictionary<string, T> - Inline object / enum schemas -> nested types under the owning model class
Naming conventions are automatically converted from snake_case to PascalCase for C# idiomatic use, while preserving the original JSON names via [JsonPropertyName].
Optional (non-required) nullable properties are annotated with [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] so they are omitted from the JSON payload when null.
OpenAPI 3.2 nullable type arrays such as type: ["string", "null"] are mapped to nullable CLR types like string?.
Inline / nested schemas
Inline object or enum schemas that appear inside property definitions, array items, or additionalProperties are emitted as nested types under the owning generated model class. This keeps the type hierarchy compact and avoids excessively long top-level type names.
public sealed class Order
{
[JsonPropertyName("status")]
public Order.StatusEnum? Status { get; init; }
// Inline enum schema generated as a nested type
public readonly record struct StatusEnum(string Value)
{
public static readonly StatusEnum Placed = new("placed");
public static readonly StatusEnum Approved = new("approved");
}
}Response types
The generated methods return different types based on the response content:
| Response Content | Return Type |
|---|---|
application/json | Task<T> with the deserialized response type |
text/plain or similar | Task<string> |
| Binary content | Task<byte[]> |
| No content (e.g. 204) | Task |
When multiple successful responses are available, OpenApiWeaver prefers a response with a body over a lower-status no-content response. For multiple response media types, JSON is preferred first, then binary, then text.
Runtime error handling
Generated clients do not call EnsureSuccessStatusCode(). Instead, they emit explicit error handling code for non-success responses so OpenAPI error definitions can be preserved.
- Non-2xx responses throw
OpenApiException - The exception includes
StatusCode,ReasonPhrase,ContentType, and the rawResponseContent - If the OpenAPI operation defines a matching error response schema, the generated code throws
OpenApiException<TError>with the deserialized error payload inError - Matching prefers exact status codes first, then wildcard patterns such as
4XX, thendefault
For JSON error responses, deserialization is attempted only when the response content type is JSON-compatible. If deserialization fails, the client falls back to the non-generic OpenApiException and preserves the raw response body for troubleshooting.
For successful JSON responses that are expected to have a body, an empty response body results in InvalidOperationException("The response body was empty.").
Example:
try
{
await client.Partners.CreatePartnerAsync(body, cancellationToken);
}
catch (OpenApiException<ValidationProblem> exception) when (exception.StatusCode == 400)
{
Console.WriteLine($"Validation failed: {exception.Error?.Message}");
}
catch (OpenApiException exception)
{
Console.WriteLine($"Request failed: {exception.StatusCode} {exception.ReasonPhrase}");
Console.WriteLine($"Content-Type: {exception.ContentType}");
Console.WriteLine(exception.ResponseContent);
}Multiple request body formats
Supports the following content types for request bodies:
| Content Type | Serialization |
|---|---|
application/json | JsonSerializer with JsonSerializerDefaults.Web |
application/x-www-form-urlencoded | FormUrlEncodedContent |
multipart/form-data | MultipartFormDataContent (supports binary file uploads) |
If a request body declares multiple supported media types, JSON is preferred when available.
Compile-time-only policy for form / multipart
For application/x-www-form-urlencoded and multipart/form-data request bodies, all code must be emittable entirely at compile time. If this is not possible, generation fails with OAW004.
Supported form and multipart request bodies require:
- A schema reference to
components/schemas - An object shape whose properties are known at generation time
- Property types that map directly to CLR types (scalars,
byte[], supported collections)
The following are intentionally unsupported for form / multipart request bodies:
- Inline request body schemas
oneOf/anyOfadditionalProperties/patternProperties
Parameter locations
Parameters defined in the OpenAPI document are mapped to method parameters based on their location:
| Location | Behavior |
|---|---|
path | Interpolated into the URL path |
query | Appended as query string parameters |
header | Added as request headers |
cookie | Added to the Cookie header |
Security scheme support
Constructor parameters are automatically generated based on the security schemes defined in the OpenAPI document:
| Scheme | Generated Parameter |
|---|---|
| OAuth2 / Bearer token | string accessToken - sent as Authorization: Bearer {token} |
| API key (header) | string apiKey - sent as a custom request header |
| API key (query) | string apiKey - appended to the query string |
| API key (cookie) | string apiKey - sent in the Cookie header |
Multiple security schemes can be combined. For example, if an API requires both an OAuth2 token and an API key, the constructor will accept both parameters.
All HTTP methods
Supports all standard HTTP methods: GET, POST, PUT, DELETE, PATCH, HEAD, OPTIONS, and TRACE.
Build-time diagnostics
Reports errors and warnings as standard compiler diagnostics during compilation:
| Code | Severity | Description |
|---|---|---|
| OAW001 | Error | OpenAPI document is empty |
| OAW002 | Warning | OpenAPI document has validation warnings |
| OAW003 | Error | OpenAPI document is invalid (e.g. malformed JSON/YAML) |
| OAW004 | Error | OpenAPI document uses an unsupported feature |
These diagnostics appear in the Visual Studio Error List, dotnet build output, and CI logs, just like any other compiler diagnostic.
Generated client extensibility
The root client class is generated as a partial class, so you can extend it with additional methods or properties in a separate file without modifying the generated code.
The root client also implements IDisposable and disposes the internal HttpClient when Dispose() is called.
XML documentation comments
The generated code includes <summary>, <remarks>, <param>, and <returns> XML doc comments derived from info, tag descriptions, operation summary / description, parameter descriptions, response summary / description, and schema title / description. HTML markup is stripped automatically so the generated IntelliSense stays clean.