diff --git a/.github/dependabot.yml b/.github/dependabot.yml
index cce780733f..18b5a96719 100644
--- a/.github/dependabot.yml
+++ b/.github/dependabot.yml
@@ -43,5 +43,5 @@ updates:
     - dependency-name: "Microsoft.AspNetCore.Mvc.NewtonsoftJson"
     - dependency-name: "Microsoft.AspNetCore.Mvc.Testing"
     - dependency-name: "Microsoft.OpenApi"
-    - dependency-name: "Microsoft.OpenApi.Readers"
+    - dependency-name: "Microsoft.OpenApi.YamlReader"
     - dependency-name: "xunit"
diff --git a/.github/workflows/dependency-review.yml b/.github/workflows/dependency-review.yml
index 0a51eb63f5..b5847a4f55 100644
--- a/.github/workflows/dependency-review.yml
+++ b/.github/workflows/dependency-review.yml
@@ -1,28 +1,28 @@
-name: dependency-review
-
-on:
-  pull_request:
-    branches: [ master, dotnet-vnext ]
-
-permissions: {}
-
-jobs:
-  dependency-review:
-    runs-on: ubuntu-latest
-
-    permissions:
-      contents: read
-
-    steps:
-
-      - name: Checkout code
-        uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
-        with:
-          filter: 'tree:0'
-          persist-credentials: false
-          show-progress: false
-
-      - name: Review dependencies
+name: dependency-review
+
+on:
+  pull_request:
+    branches: [ master, dotnet-vnext ]
+
+permissions: {}
+
+jobs:
+  dependency-review:
+    runs-on: ubuntu-latest
+
+    permissions:
+      contents: read
+
+    steps:
+
+      - name: Checkout code
+        uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
+        with:
+          filter: 'tree:0'
+          persist-credentials: false
+          show-progress: false
+
+      - name: Review dependencies
         uses: actions/dependency-review-action@40c09b7dc99638e5ddb0bfd91c1673effc064d8a # v4.8.1
-        with:
-          allow-licenses: '(MPL-2.0 OR Apache-2.0),0BSD,Apache-2.0,BSD-2-Clause,BSD-3-Clause,ISC,MIT,Python-2.0,LicenseRef-scancode-generic-cla AND MIT'
+        with:
+          allow-licenses: '(MPL-2.0 OR Apache-2.0),0BSD,Apache-2.0,BSD-2-Clause,BSD-3-Clause,ISC,MIT,Python-2.0,LicenseRef-scancode-generic-cla AND MIT'
diff --git a/Directory.Build.props b/Directory.Build.props
index 9bd7c66fa0..dd68ffe293 100644
--- a/Directory.Build.props
+++ b/Directory.Build.props
@@ -8,7 +8,7 @@
     https://github.com/domaindrivendev/Swashbuckle.AspNetCore
     true
     Copyright (c) 2016-$([System.DateTime]::Now.ToString(yyyy)) Richard Morris
-    net9.0;net8.0
+    net10.0;net9.0;net8.0
     true
     true
     $(IsPackable)
@@ -18,6 +18,8 @@
     latest
     true
     en-US
+    
+    $(NoWarn);NU5104
     direct
     
 
 ```cs
-using Microsoft.OpenApi.Models;
+using Microsoft.OpenApi;
 
 var builder = WebApplication.CreateBuilder(args);
 
@@ -106,7 +111,7 @@ Then, expose the OpenAPI JSON document endpoint(s) using one of following method
 // Your own endpoints go here, and then...
 app.MapSwagger();
 ```
-snippet source | anchor
+snippet source | anchor
 
 
 
@@ -118,7 +123,7 @@ app.MapSwagger();
 ```cs
 app.UseSwagger();
 ```
-snippet source | anchor
+snippet source | anchor
 
 
 
@@ -135,7 +140,7 @@ app.UseSwaggerUI(options =>
     options.SwaggerEndpoint("v1/swagger.json", "My API V1");
 });
 ```
-snippet source | anchor
+snippet source | anchor
 
 
 
@@ -208,7 +213,7 @@ app.UseMvc(routes =>
     routes.MapRoute("default", "{controller=Home}/{action=Index}/{id?}");
 });
 ```
-snippet source | anchor
+snippet source | anchor
 
 
 
@@ -282,6 +287,7 @@ Check out the table below for the full list of possible configuration options.
 | ------------- | ----------------------------------- |
 | **Swashbuckle.AspNetCore.Swagger**     | [Change the Path for OpenAPI JSON Endpoints](docs/configure-and-customize-swagger.md#change-the-path-for-openapi-json-endpoints)                                            |
 |                                        | [Modify OpenAPI with Request Context](docs/configure-and-customize-swagger.md#modify-openapi-with-request-context)                                                          |
+|                                        | [Serialize OpenAPI JSON in the 3.1 format](docs/configure-and-customize-swagger.md#serialize-openapi-in-the-31-format)                                                      |
 |                                        | [Serialize Swagger JSON in the 2.0 format](docs/configure-and-customize-swagger.md#serialize-swagger-in-the-20-format)                                                      |
 |                                        | [Working with Virtual Directories and Reverse Proxies](docs/configure-and-customize-swagger.md#working-with-virtual-directories-and-reverse-proxies)                        |
 |                                        | [Customizing how the OpenAPI document is serialized](docs/configure-and-customize-swagger.md#customizing-how-the-openapi-document-is-serialized)                            |
@@ -341,6 +347,7 @@ Check out the table below for the full list of possible configuration options.
 [help-wanted-badge]: https://img.shields.io/github/issues/domaindrivendev/Swashbuckle.AspNetCore/help-wanted?style=flat&color=%24EC820&label=Help%20wanted
 [help-wanted-issues]: https://github.com/domaindrivendev/Swashbuckle.AspNetCore/labels/help-wanted "Issues with help wanted for this project"
 [microelements-swashbuckle-fluentvalidation]: https://github.com/micro-elements/MicroElements.Swashbuckle.FluentValidation "MicroElements.Swashbuckle.FluentValidation on GitHub"
+[microsoft-openapi]: https://www.nuget.org/packages/Microsoft.OpenApi "The Microsoft.OpenApi NuGet package"
 [mmlib-swaggerforocelot]: MMLib.SwaggerForOcelot "MMLib.SwaggerForOcelot on GitHub"
 [mvc-routing]: https://learn.microsoft.com/aspnet/core/mvc/controllers/routing "Routing to controller actions in ASP.NET Core"
 [newtonsoft-json]: https://www.nuget.org/packages/Newtonsoft.Json/ "Newtonsoft.Json NuGet package"
@@ -365,3 +372,4 @@ Check out the table below for the full list of possible configuration options.
 [swagger-ui]: https://github.com/swagger-api/swagger-ui "The swagger-ui project in GitHub"
 [system-text-json]: https://learn.microsoft.com/en-us/dotnet/standard/serialization/system-text-json/overview "JSON serialization and deserialization in .NET - overview"
 [unchase-swashbuckle-aspnetcore-extensions]: https://github.com/unchase/Unchase.Swashbuckle.AspNetCore.Extensions "Unchase.Swashbuckle.AspNetCore.Extensions on GitHub"
+[v10-migration]: docs/migrating-to-v10.md "Migrating to Swashbuckle.AspNetCore v10"
diff --git a/docs/configure-and-customize-annotations.md b/docs/configure-and-customize-annotations.md
index 1ec8f84c95..86c041deea 100644
--- a/docs/configure-and-customize-annotations.md
+++ b/docs/configure-and-customize-annotations.md
@@ -178,17 +178,20 @@ public class Item
 ```cs
 public class ItemSchemaFilter : ISchemaFilter
 {
-    public void Apply(OpenApiSchema schema, SchemaFilterContext context)
+    public void Apply(IOpenApiSchema schema, SchemaFilterContext context)
     {
-        schema.Example = new OpenApiObject
+        if (schema is OpenApiSchema concrete)
         {
-            ["Id"] = new OpenApiInteger(1),
-            ["Description"] = new OpenApiString("An awesome item")
-        };
+            concrete.Example = new JsonObject
+            {
+                ["Id"] = 1,
+                ["Description"] = "An awesome item"
+            };
+        }
     }
 }
 ```
-snippet source | anchor
+snippet source | anchor
 
 
 
diff --git a/docs/configure-and-customize-redoc.md b/docs/configure-and-customize-redoc.md
index df34563adb..82f24a8166 100644
--- a/docs/configure-and-customize-redoc.md
+++ b/docs/configure-and-customize-redoc.md
@@ -13,7 +13,7 @@ app.UseReDoc(options =>
     options.RoutePrefix = "docs";
 });
 ```
-snippet source | anchor
+snippet source | anchor
 
 
 
@@ -30,7 +30,7 @@ app.UseReDoc(options =>
     options.DocumentTitle = "My API Docs";
 });
 ```
-snippet source | anchor
+snippet source | anchor
 
 
 
@@ -61,7 +61,7 @@ app.UseReDoc(options =>
     options.SortPropsAlphabetically();
 });
 ```
-snippet source | anchor
+snippet source | anchor
 
 
 
@@ -82,7 +82,7 @@ app.UseReDoc(options =>
     options.InjectStylesheet("/redoc/custom.css");
 });
 ```
-snippet source | anchor
+snippet source | anchor
 
 
 
@@ -101,7 +101,7 @@ app.UseReDoc(options =>
     };
 });
 ```
-snippet source | anchor
+snippet source | anchor
 
 
 
@@ -119,7 +119,7 @@ app.UseReDoc(options =>
         .GetManifestResourceStream("CustomIndex.ReDoc.index.html"); // Requires file to be added as an embedded resource
 });
 ```
-snippet source | anchor
+snippet source | anchor
 
 
 
diff --git a/docs/configure-and-customize-swagger.md b/docs/configure-and-customize-swagger.md
index ef390e0bdd..3e80e350c9 100644
--- a/docs/configure-and-customize-swagger.md
+++ b/docs/configure-and-customize-swagger.md
@@ -17,7 +17,7 @@ app.UseSwagger(options =>
     options.RouteTemplate = "api-docs/{documentName}/swagger.json";
 });
 ```
-snippet source | anchor
+snippet source | anchor
 
 
 
@@ -50,7 +50,7 @@ app.UseSwagger(options =>
     });
 });
 ```
-snippet source | anchor
+snippet source | anchor
 
 
 
@@ -58,6 +58,25 @@ The `OpenApiDocument` and the current `HttpRequest` are both passed to the filte
 For example, you can add an explicit API server based on the `Host` header (as shown), or you could inspect session
 information or an `Authorization` header and remove operations from the document based on user permissions.
 
+## Serialize OpenAPI in the 3.1 format
+
+By default, Swashbuckle.AspNetCore will generate and expose OpenAPI JSON in version 3.0 of the specification.
+However, if you wish to use the latest version of the OpenAPI specification, you can opt into version 3.1
+format with the following option:
+
+
+
+
+```cs
+app.UseSwagger(options =>
+{
+    options.OpenApiVersion = OpenApiSpecVersion.OpenApi3_1;
+});
+```
+snippet source | anchor
+
+
+
 ## Serialize Swagger in the 2.0 format
 
 By default, Swashbuckle will generate and expose OpenAPI JSON in version 3.0 of the specification, officially called the
@@ -73,7 +92,7 @@ app.UseSwagger(options =>
     options.OpenApiVersion = OpenApiSpecVersion.OpenApi2_0;
 });
 ```
-snippet source | anchor
+snippet source | anchor
 
 
 
@@ -98,7 +117,7 @@ app.UseSwaggerUI(options =>
     options.SwaggerEndpoint("v1/swagger.json", "My API V1");
 });
 ```
-snippet source | anchor
+snippet source | anchor
 
 
 
@@ -136,6 +155,6 @@ app.UseSwagger(options =>
     options.SetCustomDocumentSerializer();
 });
 ```
-snippet source | anchor
+snippet source | anchor
 
 
diff --git a/docs/configure-and-customize-swaggergen.md b/docs/configure-and-customize-swaggergen.md
index 6310c3ec27..53e187efd2 100644
--- a/docs/configure-and-customize-swaggergen.md
+++ b/docs/configure-and-customize-swaggergen.md
@@ -786,7 +786,7 @@ public class PhoneNumber
 ```cs
 services.AddSwaggerGen(options =>
 {
-    options.MapType(() => new OpenApiSchema { Type = "string" });
+    options.MapType(() => new OpenApiSchema { Type = JsonSchemaType.String });
 });
 ```
 snippet source | anchor
@@ -871,24 +871,27 @@ to inform the AutoRest tool how enums should be modelled when it generates the A
 ```cs
 public class AutoRestSchemaFilter : ISchemaFilter
 {
-    public void Apply(OpenApiSchema schema, SchemaFilterContext context)
+    public void Apply(IOpenApiSchema schema, SchemaFilterContext context)
     {
         var type = context.Type;
-        if (type.IsEnum)
+        if (type.IsEnum && schema is OpenApiSchema concrete)
         {
-            schema.Extensions.Add(
+            concrete.Extensions ??= new Dictionary();
+            concrete.Extensions.Add(
                 "x-ms-enum",
-                new OpenApiObject
-                {
-                    ["name"] = new OpenApiString(type.Name),
-                    ["modelAsString"] = new OpenApiBoolean(true)
-                }
+                new JsonNodeExtension(
+                    new JsonObject
+                    {
+                        ["name"] = type.Name,
+                        ["modelAsString"] = true
+                    }
+                )
             );
         }
     }
 }
 ```
-snippet source | anchor
+snippet source | anchor
 
 
 
@@ -919,8 +922,13 @@ so you will need [a special JsonConverter, as shown in the .NET documentation](h
 ```cs
 public class DictionaryTKeyEnumTValueSchemaFilter : ISchemaFilter
 {
-    public void Apply(OpenApiSchema schema, SchemaFilterContext context)
+    public void Apply(IOpenApiSchema schema, SchemaFilterContext context)
     {
+        if (schema is not OpenApiSchema concrete)
+        {
+            return;
+        }
+
         // Only run for fields that are a Dictionary
         if (!context.Type.IsGenericType || !context.Type.GetGenericTypeDefinition().IsAssignableFrom(typeof(Dictionary<,>)))
         {
@@ -936,14 +944,14 @@ public class DictionaryTKeyEnumTValueSchemaFilter : ISchemaFilter
             return;
         }
 
-        schema.Type = "object";
-        schema.Properties = keyType.GetEnumNames().ToDictionary(
+        concrete.Type = JsonSchemaType.Object;
+        concrete.Properties = keyType.GetEnumNames().ToDictionary(
             name => name,
             name => context.SchemaGenerator.GenerateSchema(valueType, context.SchemaRepository));
     }
 }
 ```
-snippet source | anchor
+snippet source | anchor
 
 
 
@@ -981,11 +989,11 @@ public class TagDescriptionsDocumentFilter : IDocumentFilter
 {
     public void Apply(OpenApiDocument swaggerDoc, DocumentFilterContext context)
     {
-        swaggerDoc.Tags =
-        [
-            new OpenApiTag { Name = "Products", Description = "Browse/manage the product catalog" },
-            new OpenApiTag { Name = "Orders", Description = "Submit orders" }
-        ];
+        swaggerDoc.Tags = new HashSet()
+        {
+            new() { Name = "Products", Description = "Browse/manage the product catalog" },
+            new() { Name = "Orders", Description = "Submit orders" }
+        };
     }
 }
 ```
@@ -1051,19 +1059,13 @@ services.AddSwaggerGen(options =>
 ```cs
 services.AddSwaggerGen(options =>
 {
-    options.AddSecurityRequirement(new OpenApiSecurityRequirement
+    options.AddSecurityRequirement((document) => new OpenApiSecurityRequirement()
     {
-        {
-            new OpenApiSecurityScheme
-            {
-                Reference = new OpenApiReference { Type = ReferenceType.SecurityScheme, Id = "oauth2" }
-            },
-            ["readAccess", "writeAccess"]
-        }
+        [new OpenApiSecuritySchemeReference("oauth2", document)] = ["readAccess", "writeAccess"]
     });
 });
 ```
-snippet source | anchor
+snippet source | anchor
 
 
 
@@ -1094,10 +1096,7 @@ public class SecurityRequirementsOperationFilter : IOperationFilter
             operation.Responses.Add("401", new OpenApiResponse { Description = "Unauthorized" });
             operation.Responses.Add("403", new OpenApiResponse { Description = "Forbidden" });
 
-            var scheme = new OpenApiSecurityScheme
-            {
-                Reference = new OpenApiReference { Type = ReferenceType.SecurityScheme, Id = "oauth2" }
-            };
+            var scheme = new OpenApiSecuritySchemeReference("oauth2", context.Document);
 
             operation.Security =
             [
@@ -1110,7 +1109,7 @@ public class SecurityRequirementsOperationFilter : IOperationFilter
     }
 }
 ```
-snippet source | anchor
+snippet source | anchor
 
 
 
@@ -1133,19 +1132,13 @@ services.AddSwaggerGen(options =>
         BearerFormat = "JWT",
         Description = "JWT Authorization header using the Bearer scheme."
     });
-    options.AddSecurityRequirement(new OpenApiSecurityRequirement
+    options.AddSecurityRequirement(document => new OpenApiSecurityRequirement
     {
-        {
-            new OpenApiSecurityScheme
-            {
-                Reference = new OpenApiReference { Type = ReferenceType.SecurityScheme, Id = "bearer" }
-            },
-            []
-        }
+        [new OpenApiSecuritySchemeReference("bearer", document)] = []
     });
 });
 ```
-snippet source | anchor
+snippet source | anchor
 
 
 
@@ -1237,7 +1230,7 @@ services.AddSwaggerGen(options =>
     });
 });
 ```
-snippet source | anchor
+snippet source | anchor
 
 
 
@@ -1319,7 +1312,7 @@ services.AddSwaggerGen(options =>
     options.SelectDiscriminatorValueUsing((subType) => subType.Name);
 });
 ```
-snippet source | anchor
+snippet source | anchor
 
 
 
diff --git a/docs/configure-and-customize-swaggerui.md b/docs/configure-and-customize-swaggerui.md
index 27af2bcac2..8dfcf90efa 100644
--- a/docs/configure-and-customize-swaggerui.md
+++ b/docs/configure-and-customize-swaggerui.md
@@ -13,7 +13,7 @@ app.UseSwaggerUI(options =>
     options.RoutePrefix = "api-docs";
 });
 ```
-snippet source | anchor
+snippet source | anchor
 
 
 
@@ -31,7 +31,7 @@ app.UseSwaggerUI(options =>
     options.DocumentTitle = "My Swagger UI";
 });
 ```
-snippet source | anchor
+snippet source | anchor
 
 
 
@@ -51,7 +51,7 @@ app.UseSwaggerUI(options =>
     options.ScriptPresetsPath = "https://cdnjs.cloudflare.com/ajax/libs/swagger-ui/5.29.1/swagger-ui-standalone-preset.min.js";
 });
 ```
-snippet source | anchor
+snippet source | anchor
 
 
 
@@ -71,7 +71,7 @@ app.UseSwaggerUI(options =>
     options.SwaggerEndpoint("/swagger/v2/swagger.json", "V2 Docs");
 });
 ```
-snippet source | anchor
+snippet source | anchor
 
 
 
@@ -106,7 +106,7 @@ app.UseSwaggerUI(options =>
     options.UseResponseInterceptor("(response) => { return response; }");
 });
 ```
-snippet source | anchor
+snippet source | anchor
 
 
 
@@ -124,7 +124,7 @@ app.UseSwaggerUI(options =>
     options.InjectJavascript("/swagger-ui/custom.js");
 });
 ```
-snippet source | anchor
+snippet source | anchor
 
 
 
@@ -142,7 +142,7 @@ app.UseSwaggerUI(options =>
     options.InjectStylesheet("/swagger-ui/custom.css");
 });
 ```
-snippet source | anchor
+snippet source | anchor
 
 
 
@@ -160,7 +160,7 @@ app.UseSwaggerUI(options =>
         .GetManifestResourceStream("CustomUIIndex.Swagger.index.html"); // Requires file to be added as an embedded resource
 });
 ```
-snippet source | anchor
+snippet source | anchor
 
 
 
@@ -205,7 +205,7 @@ app.UseSwaggerUI(options =>
     options.OAuthUsePkce();
 });
 ```
-snippet source | anchor
+snippet source | anchor
 
 
 
@@ -224,7 +224,7 @@ app.UseSwaggerUI(options =>
     options.UseResponseInterceptor("(res) => { console.log('Custom interceptor intercepted response from:', res.url); return res; }");
 });
 ```
-snippet source | anchor
+snippet source | anchor
 
 
 
@@ -239,7 +239,7 @@ app.UseSwaggerUI(options =>
     options.UseRequestInterceptor("(req) => { req.headers['X-XSRF-Token'] = localStorage.getItem('xsrf-token'); return req; }");
 });
 ```
-snippet source | anchor
+snippet source | anchor
 
 
 
diff --git a/docs/migrating-to-v10.md b/docs/migrating-to-v10.md
new file mode 100644
index 0000000000..472adc7b27
--- /dev/null
+++ b/docs/migrating-to-v10.md
@@ -0,0 +1,85 @@
+# Migrating to Swashbuckle.AspNetCore v10
+
+> [!IMPORTANT]  
+> This document describes the breaking changes introduced in Swashbuckle.AspNetCore v10, and how to migrate from v9.x to v10+.
+
+## Why breaking changes?
+
+While the [OpenAPI 3.1 specification][openapi-specification] is a minor release compared to OpenAPI 3.0, the OpenAPI specification does
+not use Semantic Versioning (SemVer). The changes introduced between the two versions are quite breaking in a practical sense, so major
+changes were required to be made to [Microsoft.OpenApi][microsoft-openapi-package], the package which Swashbuckle.AspNetCore builds upon,
+in order to allow applications to produce OpenAPI 3.1 documents.
+
+These changes were introduced in [Microsoft.OpenApi v2.0.0][microsoft-openapi-v2-migration-guide], which _does_ follow SemVer. As a result,
+Swashbuckle.AspNetCore v10+ now depends on Microsoft.OpenApi v2+ to allow users to produce OpenAPI 3.1 documents, fulfilling a
+long-standing feature request: [Plans on official support for OpenApi 3.1.0 #2349][feature-request].
+
+These changes are unfortunately required, even if you still wish to target Swagger 2.0 or OpenAPI 3.0 documents, as the same library is used
+to produce all three document format versions.
+
+For the same breaking changes, ASP.NET Core v10+ also depends on Microsoft.OpenApi v2+, so these changes were also required to allow applications
+using ASP.NET Core 10 to use Swashbuckle.AspNetCore effectively with minimal friction. This also helps support users who may wish to migrate an
+application from Swashbuckle.AspNetCore to Microsoft.AspNetCore.OpenApi (for example if they need native AoT support). More information about the
+breaking changes in ASP.NET Core 10 can be found in this document: _[What's new in ASP.NET Core in .NET 10][breaking-changes-aspnetcore]_.
+
+The refactoring required to support OpenAPI 3.1 in Swashbuckle.AspNetCore was significant. If you're interested in what exactly what was changed,
+you can check out the PR to implement it that was worked on over the course of .NET 10's development (it's quite large): _[Support .NET 10 #3283][swashbuckle-aspnetcore-10]_.
+
+## How do I enable OpenAPI 3.1 support?
+
+By default, to minimise breaking _behavioural_ changes, Swashbuckle.AspNetCore v10+ will continue to produce OpenAPI 3.0 documents by default.
+
+To upgrade your OpenAPI documents to output using version 3.1 of the OpenAPI specification, you can override the version as shown in the code snippet below.
+
+
+
+
+```cs
+app.UseSwagger(options =>
+{
+    options.OpenApiVersion = OpenApiSpecVersion.OpenApi3_1;
+});
+```
+snippet source | anchor
+
+
+
+## How do I migrate to Swashbuckle.AspNetCore v10+?
+
+The majority of the breaking changes you may encounter when migrating to Swashbuckle.AspNetCore v10+ are due to the underlying
+changes in Microsoft.OpenApi v2.0.0+. The [v2 migration guide for Microsoft.OpenApi][microsoft-openapi-v2-migration-guide] provides a
+detailed overview of the breaking changes and how to migrate your code from Microsoft.OpenApi v1.x to v2.x. The Microsoft.OpenApi
+breaking changes document should be your primary reference when migrating to Swashbuckle.AspNetCore v10+.
+
+The chances of encountering breaking changes when migrating to Swashbuckle.AspNetCore v10+ depend on how extensively you use the
+Swashbuckle.AspNetCore extensibility points, such as custom filters, to generate the OpenAPI document. If you do not use any custom logic
+and do not depend on any other third-party libraries that depend on Microsoft.OpenApi v1.x, you may not encounter any breaking changes at all.
+
+In cases where Swashbuckle.AspNetCore has a breaking change, this is because the types from the underlying Microsoft.OpenApi library
+are exposed in the public API of Swashbuckle.AspNetCore, so the breaking change bubbles up through the public API surface to cause compilation
+issues in your code. For example the signature for `IRequestBodyAsyncFilter.ApplyAsync()` has changed as shown below:
+
+```diff
+- Swashbuckle.AspNetCore.SwaggerGen.IRequestBodyAsyncFilter.ApplyAsync(Microsoft.OpenApi.Models.OpenApiRequestBody requestBody, Swashbuckle.AspNetCore.SwaggerGen.RequestBodyFilterContext context, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task
++ Swashbuckle.AspNetCore.SwaggerGen.IRequestBodyAsyncFilter.ApplyAsync(Microsoft.OpenApi.IOpenApiRequestBody requestBody, Swashbuckle.AspNetCore.SwaggerGen.RequestBodyFilterContext context, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task
+```
+
+## Migration Overview
+
+Migrating to Swashbuckle.AspNetCore v10+ will likely involve changes in the following areas:
+
+- Update any NuGet package references for Swashbuckle.AspNetCore and Microsoft.OpenApi to v10+ and v2.x.x respectively.
+- Update any `using` directives that reference types from the `Microsoft.OpenApi.Models` namespace to use the new namespace `Microsoft.OpenApi`.
+- Update model references (e.g. `OpenApiSchema`) to use the new interfaces (e.g. `IOpenApiSchema`) and the relevant concrete types to mutate them (e.g. `OpenApiSchema`).
+- Update any use of `.Reference` properties (e.g. `OpenApiSchema.ReferenceV3`) to use the new `*Reference` class instead (e.g. `OpenApiSchemaReference`).
+- Replace usage of the `OpenApiSchema.Type` property using a string (e.g. `"string"` or `"boolean"`) with the `JsonSchemaType` flags enumeration.
+- Replace usage of the `OpenApiSchema.Nullable` property by OR-ing the `JsonSchemaType.Null` value to `OpenApiSchema.Type` (e.g. `schema.Type |= JsonSchemaType.Null;`).
+- Remove any use of the [now-deprecated `WithOpenApi()` extension method][withopenapi-deprecation] in Microsoft.AspNetCore.OpenApi.
+
+[breaking-changes-aspnetcore]: https://learn.microsoft.com/aspnet/core/release-notes/aspnetcore-10.0?#openapi-31-breaking-changes "OpenAPI 3.1 breaking changes"
+[feature-request]: https://github.com/domaindrivendev/Swashbuckle.AspNetCore/issues/2349 "Plans on official support for OpenApi 3.1.0"
+[microsoft-openapi-package]: https://www.nuget.org/packages/Microsoft.OpenApi/ "Microsoft.OpenApi NuGet package"
+[microsoft-openapi-v2-migration-guide]: https://github.com/microsoft/OpenAPI.NET/blob/main/docs/upgrade-guide-2.md "Microsoft OpenAPI.NET v2 migration guide"
+[openapi-specification]: https://swagger.io/specification/ "OpenAPI Specification"
+[swashbuckle-aspnetcore-10]: https://github.com/domaindrivendev/Swashbuckle.AspNetCore/pull/3283 "Support .NET 10"
+[withopenapi-deprecation]: https://github.com/aspnet/Announcements/issues/519 "[Breaking change]: Deprecation of WithOpenApi extension method"
diff --git a/global.json b/global.json
index 909a1fb9ce..bcd874c697 100644
--- a/global.json
+++ b/global.json
@@ -1,6 +1,6 @@
 {
   "sdk": {
-    "version": "9.0.306",
+    "version": "10.0.100-rc.2.25502.107",
     "allowPrerelease": false,
     "rollForward": "latestMajor",
     "paths": [ ".dotnet", "$host$" ]
diff --git a/perf/Swashbuckle.AspNetCore.Benchmarks/Swashbuckle.AspNetCore.Benchmarks.csproj b/perf/Swashbuckle.AspNetCore.Benchmarks/Swashbuckle.AspNetCore.Benchmarks.csproj
index 718d1dfc14..b876a81442 100644
--- a/perf/Swashbuckle.AspNetCore.Benchmarks/Swashbuckle.AspNetCore.Benchmarks.csproj
+++ b/perf/Swashbuckle.AspNetCore.Benchmarks/Swashbuckle.AspNetCore.Benchmarks.csproj
@@ -2,7 +2,7 @@
   
     false
     Exe
-    net9.0
+    net10.0
     True
     $(MSBuildThisFileDirectory)..\..\src\Swashbuckle.AspNetCore.Swagger\Swashbuckle.AspNetCore.Swagger.snk
   
diff --git a/perf/Swashbuckle.AspNetCore.Benchmarks/XmlCommentsBenchmark.cs b/perf/Swashbuckle.AspNetCore.Benchmarks/XmlCommentsBenchmark.cs
index f50fe1f485..ccdb2cbb76 100644
--- a/perf/Swashbuckle.AspNetCore.Benchmarks/XmlCommentsBenchmark.cs
+++ b/perf/Swashbuckle.AspNetCore.Benchmarks/XmlCommentsBenchmark.cs
@@ -4,7 +4,7 @@
 using BenchmarkDotNet.Attributes;
 using Microsoft.AspNetCore.Mvc.ApiExplorer;
 using Microsoft.AspNetCore.Mvc.Controllers;
-using Microsoft.OpenApi.Models;
+using Microsoft.OpenApi;
 using Swashbuckle.AspNetCore.SwaggerGen;
 using Swashbuckle.AspNetCore.SwaggerGen.Test;
 using Swashbuckle.AspNetCore.TestSupport;
@@ -57,7 +57,9 @@ public void Setup()
         using var xmlStream = new MemoryStream();
         xmlDocument.Save(xmlStream);
         xmlStream.Seek(0, SeekOrigin.Begin);
+
         var xPathDocument = new XPathDocument(xmlStream);
+        var members = XmlCommentsDocumentHelper.CreateMemberDictionary(xPathDocument);
 
         // Document
         _document = new OpenApiDocument();
@@ -83,7 +85,7 @@ public void Setup()
             null,
             null);
 
-        _documentFilter = new XmlCommentsDocumentFilter(xPathDocument);
+        _documentFilter = new XmlCommentsDocumentFilter(members, new());
 
         // Operation
         _operation = new OpenApiOperation();
@@ -91,13 +93,13 @@ public void Setup()
             .GetMethod(nameof(FakeConstructedControllerWithXmlComments.ActionWithSummaryAndResponseTags));
 
         var apiDescription = ApiDescriptionFactory.Create(methodInfo: methodInfo, groupName: "v1", httpMethod: "POST", relativePath: "resource");
-        _operationFilterContext = new OperationFilterContext(apiDescription, null, null, methodInfo);
-        _operationFilter = new XmlCommentsOperationFilter(xPathDocument);
+        _operationFilterContext = new OperationFilterContext(apiDescription, null, null, null, methodInfo);
+        _operationFilter = new XmlCommentsOperationFilter(members, new());
 
         // Parameter
         _parameter = new()
         {
-            Schema = new()
+            Schema = new OpenApiSchema()
             {
                 Type = JsonSchemaTypes.String,
                 Description = "schema-level description",
@@ -106,8 +108,9 @@ public void Setup()
 
         var propertyInfo = typeof(XmlAnnotatedType).GetProperty(nameof(XmlAnnotatedType.StringProperty));
         var apiParameterDescription = new ApiParameterDescription();
-        _parameterFilterContext = new ParameterFilterContext(apiParameterDescription, null, null, propertyInfo: propertyInfo);
-        _parameterFilter = new XmlCommentsParameterFilter(xPathDocument);
+        var xmlDocMembers = XmlCommentsDocumentHelper.CreateMemberDictionary(xPathDocument);
+        _parameterFilterContext = new ParameterFilterContext(apiParameterDescription, null, null, null, propertyInfo: propertyInfo);
+        _parameterFilter = new XmlCommentsParameterFilter(xmlDocMembers, new());
 
         // Request Body
         _requestBody = new OpenApiRequestBody
@@ -116,7 +119,7 @@ public void Setup()
             {
                 ["application/json"] = new()
                 {
-                    Schema = new()
+                    Schema = new OpenApiSchema()
                     {
                         Type = JsonSchemaTypes.String,
                     },
@@ -131,8 +134,8 @@ public void Setup()
         {
             ParameterDescriptor = new ControllerParameterDescriptor { ParameterInfo = parameterInfo }
         };
-        _requestBodyFilterContext = new RequestBodyFilterContext(bodyParameterDescription, null, null, null);
-        _requestBodyFilter = new XmlCommentsRequestBodyFilter(xPathDocument);
+        _requestBodyFilterContext = new RequestBodyFilterContext(bodyParameterDescription, null, null, null, null);
+        _requestBodyFilter = new XmlCommentsRequestBodyFilter(xmlDocMembers, new());
     }
 
     [Benchmark]
diff --git a/src/Shared/JsonExtensions.cs b/src/Shared/JsonExtensions.cs
new file mode 100644
index 0000000000..cd314a1f5e
--- /dev/null
+++ b/src/Shared/JsonExtensions.cs
@@ -0,0 +1,26 @@
+using System.Text.Json;
+using System.Text.Json.Nodes;
+
+namespace Swashbuckle.AspNetCore;
+
+internal static class JsonExtensions
+{
+    private static readonly JsonSerializerOptions Options = new()
+    {
+#if NET9_0_OR_GREATER
+        NewLine = "\n",
+#endif
+        WriteIndented = true,
+    };
+
+    public static string ToJson(this JsonNode value)
+    {
+        var json = value.ToJsonString(Options);
+
+#if !NET9_0_OR_GREATER
+        json = json.Replace("\r\n", "\n");
+#endif
+
+        return json;
+    }
+}
diff --git a/src/Shared/JsonSchemaTypes.cs b/src/Shared/JsonSchemaTypes.cs
index 7285f9e9e1..693640ceca 100644
--- a/src/Shared/JsonSchemaTypes.cs
+++ b/src/Shared/JsonSchemaTypes.cs
@@ -1,12 +1,14 @@
+using Microsoft.OpenApi;
+
 namespace Swashbuckle.AspNetCore;
 
 internal static class JsonSchemaTypes
 {
-    public const string Array = "array";
-    public const string Boolean = "boolean";
-    public const string Integer = "integer";
-    public const string Number = "number";
-    public const string Null = "null";
-    public const string Object = "object";
-    public const string String = "string";
+    public static readonly JsonSchemaType Array = JsonSchemaType.Array;
+    public static readonly JsonSchemaType Boolean = JsonSchemaType.Boolean;
+    public static readonly JsonSchemaType Integer = JsonSchemaType.Integer;
+    public static readonly JsonSchemaType Number = JsonSchemaType.Number;
+    public static readonly JsonSchemaType Null = JsonSchemaType.Null;
+    public static readonly JsonSchemaType Object = JsonSchemaType.Object;
+    public static readonly JsonSchemaType String = JsonSchemaType.String;
 }
diff --git a/src/Swashbuckle.AspNetCore.Annotations/AnnotationsDocumentFilter.cs b/src/Swashbuckle.AspNetCore.Annotations/AnnotationsDocumentFilter.cs
index a54ed0e838..412780804c 100644
--- a/src/Swashbuckle.AspNetCore.Annotations/AnnotationsDocumentFilter.cs
+++ b/src/Swashbuckle.AspNetCore.Annotations/AnnotationsDocumentFilter.cs
@@ -1,5 +1,5 @@
 using Microsoft.AspNetCore.Mvc.Controllers;
-using Microsoft.OpenApi.Models;
+using Microsoft.OpenApi;
 using Swashbuckle.AspNetCore.SwaggerGen;
 
 namespace Swashbuckle.AspNetCore.Annotations;
@@ -8,7 +8,7 @@ public class AnnotationsDocumentFilter : IDocumentFilter
 {
     public void Apply(OpenApiDocument swaggerDoc, DocumentFilterContext context)
     {
-        swaggerDoc.Tags ??= [];
+        swaggerDoc.Tags ??= new SortedSet();
 
         // Collect (unique) controller names and custom attributes in a dictionary
         var controllerNamesAndAttributes = context.ApiDescriptions
@@ -24,7 +24,7 @@ public void Apply(OpenApiDocument swaggerDoc, DocumentFilterContext context)
     }
 
     private static void ApplySwaggerTagAttribute(
-        OpenApiDocument swaggerDoc,
+        OpenApiDocument document,
         string controllerName,
         IEnumerable