diff --git a/src/Microsoft.OpenApi/Converters/OpenApiSchemaJsonConverter.cs b/src/Microsoft.OpenApi/Converters/OpenApiSchemaJsonConverter.cs
new file mode 100644
index 000000000..37e2d8b55
--- /dev/null
+++ b/src/Microsoft.OpenApi/Converters/OpenApiSchemaJsonConverter.cs
@@ -0,0 +1,103 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT license.
+
+using System;
+using System.IO;
+using System.Text;
+using System.Text.Json;
+using System.Text.Json.Serialization;
+
+namespace Microsoft.OpenApi
+{
+ ///
+ /// Enables System.Text.Json serialization and deserialization of
+ /// using the OpenAPI wire format rather than the default reflection-based output.
+ ///
+ ///
+ /// Register this converter via :
+ ///
+ /// var options = new JsonSerializerOptions();
+ /// options.Converters.Add(new OpenApiSchemaJsonConverter());
+ /// var json = JsonSerializer.Serialize(schema, options);
+ ///
+ ///
+ public sealed class OpenApiSchemaJsonConverter : JsonConverter
+ {
+ private readonly OpenApiSpecVersion _version;
+
+ ///
+ /// Initializes a new instance of targeting OpenAPI 3.1.
+ ///
+ public OpenApiSchemaJsonConverter() : this(OpenApiSpecVersion.OpenApi3_1) { }
+
+ ///
+ /// Initializes a new instance of targeting the specified OpenAPI version.
+ ///
+ /// The OpenAPI specification version to use when serializing the schema.
+ public OpenApiSchemaJsonConverter(OpenApiSpecVersion version)
+ {
+ _version = version;
+ }
+
+ ///
+ ///
+ /// Deserializes a bare JSON Schema object into an by temporarily
+ /// embedding it in a minimal OpenAPI 3.1 document for parsing.
+ ///
+ public override OpenApiSchema? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
+ {
+ using var document = JsonDocument.ParseValue(ref reader);
+ var schemaJson = document.RootElement.GetRawText();
+
+ var wrapper = string.Concat(
+ "{\"openapi\":\"3.1.0\",\"info\":{\"title\":\"temp\",\"version\":\"0.0.0\"},",
+ "\"components\":{\"schemas\":{\"schema\":", schemaJson, "}}}");
+
+ var result = OpenApiDocument.Parse(wrapper);
+ IOpenApiSchema? schema = null;
+ result.Document?.Components?.Schemas?.TryGetValue("schema", out schema);
+ return schema as OpenApiSchema;
+ }
+
+ ///
+ public override void Write(Utf8JsonWriter writer, OpenApiSchema value, JsonSerializerOptions options)
+ {
+ Utils.CheckArgumentNull(writer);
+ Utils.CheckArgumentNull(value);
+
+ using var stream = new MemoryStream();
+ using (var textWriter = new StreamWriter(stream, Encoding.UTF8, bufferSize: 1024, leaveOpen: true))
+ {
+ var openApiWriter = new OpenApiJsonWriter(textWriter);
+ SerializeSchema(value, openApiWriter);
+ textWriter.Flush();
+ }
+
+ stream.Position = 0;
+ using var document = JsonDocument.Parse(stream);
+ document.RootElement.WriteTo(writer);
+ }
+
+ private void SerializeSchema(OpenApiSchema schema, OpenApiJsonWriter writer)
+ {
+ switch (_version)
+ {
+ case OpenApiSpecVersion.OpenApi3_2:
+ schema.SerializeAsV32(writer);
+ break;
+ case OpenApiSpecVersion.OpenApi3_1:
+ schema.SerializeAsV31(writer);
+ break;
+ case OpenApiSpecVersion.OpenApi3_0:
+ schema.SerializeAsV3(writer);
+ break;
+ case OpenApiSpecVersion.OpenApi2_0:
+ schema.SerializeAsV2(writer);
+ break;
+ default:
+ throw new ArgumentOutOfRangeException(nameof(_version), _version,
+ string.Format(SRResource.OpenApiSpecVersionNotSupported, _version));
+ }
+ }
+ }
+}
diff --git a/src/Microsoft.OpenApi/PublicAPI.Unshipped.txt b/src/Microsoft.OpenApi/PublicAPI.Unshipped.txt
index 7dc5c5811..9dee238ef 100644
--- a/src/Microsoft.OpenApi/PublicAPI.Unshipped.txt
+++ b/src/Microsoft.OpenApi/PublicAPI.Unshipped.txt
@@ -1 +1,6 @@
#nullable enable
+Microsoft.OpenApi.OpenApiSchemaJsonConverter
+Microsoft.OpenApi.OpenApiSchemaJsonConverter.OpenApiSchemaJsonConverter() -> void
+Microsoft.OpenApi.OpenApiSchemaJsonConverter.OpenApiSchemaJsonConverter(Microsoft.OpenApi.OpenApiSpecVersion version) -> void
+override Microsoft.OpenApi.OpenApiSchemaJsonConverter.Read(ref System.Text.Json.Utf8JsonReader reader, System.Type! typeToConvert, System.Text.Json.JsonSerializerOptions! options) -> Microsoft.OpenApi.OpenApiSchema?
+override Microsoft.OpenApi.OpenApiSchemaJsonConverter.Write(System.Text.Json.Utf8JsonWriter! writer, Microsoft.OpenApi.OpenApiSchema! value, System.Text.Json.JsonSerializerOptions! options) -> void
diff --git a/test/Microsoft.OpenApi.Tests/Converters/OpenApiSchemaJsonConverterTests.cs b/test/Microsoft.OpenApi.Tests/Converters/OpenApiSchemaJsonConverterTests.cs
new file mode 100644
index 000000000..18454f54c
--- /dev/null
+++ b/test/Microsoft.OpenApi.Tests/Converters/OpenApiSchemaJsonConverterTests.cs
@@ -0,0 +1,147 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT license.
+
+using System.Collections.Generic;
+using System.Text.Json;
+using FluentAssertions;
+using Xunit;
+
+namespace Microsoft.OpenApi.Tests.Converters
+{
+ [Collection("DefaultSettings")]
+ public class OpenApiSchemaJsonConverterTests
+ {
+ private static readonly JsonSerializerOptions _optionsV31 = new()
+ {
+ Converters = { new OpenApiSchemaJsonConverter(OpenApiSpecVersion.OpenApi3_1) }
+ };
+
+ private static readonly JsonSerializerOptions _optionsV3 = new()
+ {
+ Converters = { new OpenApiSchemaJsonConverter(OpenApiSpecVersion.OpenApi3_0) }
+ };
+
+ [Fact]
+ public void Serialize_SimpleStringSchema_ProducesOpenApiWireFormat()
+ {
+ var schema = new OpenApiSchema
+ {
+ Type = JsonSchemaType.String,
+ Description = "A simple string"
+ };
+
+ var json = JsonSerializer.Serialize(schema, _optionsV31);
+
+ using var doc = JsonDocument.Parse(json);
+ doc.RootElement.GetProperty("type").GetString().Should().Be("string");
+ doc.RootElement.GetProperty("description").GetString().Should().Be("A simple string");
+ }
+
+ [Fact]
+ public void Serialize_SchemaWithProperties_ProducesCorrectJson()
+ {
+ var schema = new OpenApiSchema
+ {
+ Type = JsonSchemaType.Object,
+ Properties = new Dictionary
+ {
+ ["name"] = new OpenApiSchema { Type = JsonSchemaType.String },
+ ["age"] = new OpenApiSchema { Type = JsonSchemaType.Integer }
+ }
+ };
+
+ var json = JsonSerializer.Serialize(schema, _optionsV31);
+
+ using var doc = JsonDocument.Parse(json);
+ doc.RootElement.GetProperty("type").GetString().Should().Be("object");
+ doc.RootElement.GetProperty("properties").EnumerateObject().Should().HaveCount(2);
+ }
+
+ [Fact]
+ public void Serialize_DefaultConstructor_TargetsV31()
+ {
+ var converter = new OpenApiSchemaJsonConverter();
+ var options = new JsonSerializerOptions { Converters = { converter } };
+
+ var schema = new OpenApiSchema { Type = JsonSchemaType.Boolean };
+ var json = JsonSerializer.Serialize(schema, options);
+
+ json.Should().Contain("\"type\"");
+ }
+
+ [Fact]
+ public void Deserialize_SimpleStringSchema_ReturnsCorrectSchema()
+ {
+ const string json = """{"type":"string","description":"A simple string"}""";
+
+ var schema = JsonSerializer.Deserialize(json, _optionsV31);
+
+ schema.Should().NotBeNull();
+ schema!.Type.Should().Be(JsonSchemaType.String);
+ schema.Description.Should().Be("A simple string");
+ }
+
+ [Fact]
+ public void Deserialize_SchemaWithEnum_ReturnsCorrectSchema()
+ {
+ const string json = """{"type":"string","enum":["active","inactive"]}""";
+
+ var schema = JsonSerializer.Deserialize(json, _optionsV31);
+
+ schema.Should().NotBeNull();
+ schema!.Enum.Should().HaveCount(2);
+ }
+
+ [Fact]
+ public void RoundTrip_ComplexSchema_PreservesData()
+ {
+ var original = new OpenApiSchema
+ {
+ Type = JsonSchemaType.Object,
+ Title = "User",
+ Description = "A user object",
+ Required = new System.Collections.Generic.HashSet { "name" },
+ Properties = new Dictionary
+ {
+ ["name"] = new OpenApiSchema { Type = JsonSchemaType.String },
+ ["age"] = new OpenApiSchema { Type = JsonSchemaType.Integer | JsonSchemaType.Null }
+ }
+ };
+
+ var json = JsonSerializer.Serialize(original, _optionsV31);
+ var deserialized = JsonSerializer.Deserialize(json, _optionsV31);
+
+ deserialized.Should().NotBeNull();
+ deserialized!.Title.Should().Be("User");
+ deserialized.Description.Should().Be("A user object");
+ deserialized.Properties.Should().ContainKey("name");
+ deserialized.Properties.Should().ContainKey("age");
+ }
+
+ [Fact]
+ public void Serialize_NullSchema_WritesNullLiteral()
+ {
+ // System.Text.Json handles null at the serializer level before invoking the converter,
+ // producing a JSON null literal rather than throwing.
+ var json = JsonSerializer.Serialize(null!, _optionsV31);
+
+ json.Should().Be("null");
+ }
+
+ [Fact]
+ public void Serialize_V31Schema_IncludesJsonSchemaKeywords()
+ {
+ var schema = new OpenApiSchema
+ {
+ Type = JsonSchemaType.String,
+ Id = "https://example.com/schema"
+ };
+
+ var jsonV31 = JsonSerializer.Serialize(schema, _optionsV31);
+
+ using var doc = JsonDocument.Parse(jsonV31);
+ // $id is a JSON Schema 2020-12 keyword only written in v3.1+
+ doc.RootElement.TryGetProperty("$id", out _).Should().BeTrue("$id is a v3.1 JSON Schema keyword");
+ }
+ }
+}