Skip to content

feat: add JsonConverter for OpenApiSchema System.Text.Json serialization#2915

Open
Mahdigln wants to merge 1 commit into
microsoft:mainfrom
Mahdigln:feature/openapi-schema-json-converter
Open

feat: add JsonConverter for OpenApiSchema System.Text.Json serialization#2915
Mahdigln wants to merge 1 commit into
microsoft:mainfrom
Mahdigln:feature/openapi-schema-json-converter

Conversation

@Mahdigln

@Mahdigln Mahdigln commented Jun 29, 2026

Copy link
Copy Markdown

Description

Adds OpenApiSchemaJsonConverter, a System.Text.Json.JsonConverter<OpenApiSchema> that produces clean OpenAPI wire format output instead of the default verbose reflection-based serialization.

Type of Change

  • New feature (non-breaking change which adds functionality)

Related Issue(s)

Closes #2299

Changes Made

  • Added OpenApiSchemaJsonConverter in src/Microsoft.OpenApi/Converters/
  • Supports all OpenAPI versions: 2.0, 3.0, 3.1 (default), 3.2
  • Added Read (deserialization) support in addition to Write (serialization)
  • Updated PublicAPI.Unshipped.txt with new public API surface

Testing

  • Unit tests added/updated
  • All existing tests pass

Checklist

  • My code follows the code style of this project
  • I have performed a self-review of my own code
  • My changes generate no new warnings
  • I have added tests that prove my fix is effective or that my feature works
  • New and existing unit tests pass locally with my changes

Versions applicability

  • My change applies to the version 2.X of the library, if so PR link:
  • My change applies to the version 3.X of the library, if so PR link:
  • I have evaluated the applicability of my change against the other versions above.

Additional Notes

Usage example:

var options = new JsonSerializerOptions();
options.Converters.Add(new OpenApiSchemaJsonConverter());

// Serialize
var json = JsonSerializer.Serialize(schema, options);

// Deserialize
var schema = JsonSerializer.Deserialize<OpenApiSchema>(json, options);

// Target a specific OpenAPI version
options.Converters.Add(new OpenApiSchemaJsonConverter(OpenApiSpecVersion.OpenApi3_0));

@Mahdigln Mahdigln requested a review from a team as a code owner June 29, 2026 10:11

@baywet baywet left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the contribution!

Let's a couple of comments to help get this merged

Comment on lines +36 to +37
doc.RootElement.GetProperty("type").GetString().Should().Be("string");
doc.RootElement.GetProperty("description").GetString().Should().Be("A simple string");

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

let's use the assert API. I thought we had gotten rid of FluentAssertions?

/// <summary>
/// Initializes a new instance of <see cref="OpenApiSchemaJsonConverter"/> targeting OpenAPI 3.1.
/// </summary>
public OpenApiSchemaJsonConverter() : this(OpenApiSpecVersion.OpenApi3_1) { }

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The default should be 3.2 like it is for serialization helpers. (maybe all those should rely on a common constant?)

var schemaJson = document.RootElement.GetRawText();

var wrapper = string.Concat(
"{\"openapi\":\"3.1.0\",\"info\":{\"title\":\"temp\",\"version\":\"0.0.0\"},",

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is going to break down based on the version that's passed in the constructor.
The templates should either be version specific or the constructor should guard against versions that are not supported.

/// var json = JsonSerializer.Serialize(schema, options);
/// </code>
/// </remarks>
public sealed class OpenApiSchemaJsonConverter : JsonConverter<OpenApiSchema>

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in theory this could be expanded to any model from this library (operations, path items, etc)

"{\"openapi\":\"3.1.0\",\"info\":{\"title\":\"temp\",\"version\":\"0.0.0\"},",
"\"components\":{\"schemas\":{\"schema\":", schemaJson, "}}}");

var result = OpenApiDocument.Parse(wrapper);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why not use the model factory instead of creating an empty document?


var result = OpenApiDocument.Parse(wrapper);
IOpenApiSchema? schema = null;
result.Document?.Components?.Schemas?.TryGetValue("schema", out schema);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This might break if any reference is used, the unit tests should at least account for this.

public override OpenApiSchema? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
using var document = JsonDocument.ParseValue(ref reader);
var schemaJson = document.RootElement.GetRawText();

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If this ends up in a hot path, it'll lead to a lot of string allocations

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

OpenApiSchema System.Text.Json (De)Serialization

2 participants