From 021a0c0c704d520e5870137b1b4436276a262625 Mon Sep 17 00:00:00 2001 From: Sjoerd van der Meer Date: Sun, 19 Oct 2025 11:34:07 +0200 Subject: [PATCH 1/5] OpenAPI schemas: Add failing test for circular references with an array --- .../OpenApiSchemaReferenceTransformerTests.cs | 50 +++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Transformers/Implementations/OpenApiSchemaReferenceTransformerTests.cs b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Transformers/Implementations/OpenApiSchemaReferenceTransformerTests.cs index 2979e5198444..8c1ca9d06a6f 100644 --- a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Transformers/Implementations/OpenApiSchemaReferenceTransformerTests.cs +++ b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Transformers/Implementations/OpenApiSchemaReferenceTransformerTests.cs @@ -1122,6 +1122,38 @@ await VerifyOpenApiDocument(builder, document => }); } + // Test for: https://github.com/dotnet/aspnetcore/issues/64048 + public static object[][] CircularReferencesWithArraysHandlers => + [ + [(CircularReferenceWithArrayRootOrderArrayFirst dto) => { }], + [(CircularReferenceWithArrayRootOrderArrayLast dto) => { }], + ]; + + [Theory] + [MemberData(nameof(CircularReferencesWithArraysHandlers))] + public async Task HandlesCircularReferencesWithArraysRegardlessOfPropertyOrder(Delegate requestHandler) + { + var builder = CreateBuilder(); + builder.MapPost("/", requestHandler); + + await VerifyOpenApiDocument(builder, (OpenApiDocument document) => + { + Assert.NotNull(document.Components?.Schemas); + var schema = document.Components.Schemas["CircularReferenceWithArrayModel"]; + Assert.Equal(JsonSchemaType.Object, schema.Type); + Assert.NotNull(schema.Properties); + Assert.Collection(schema.Properties, + property => + { + Assert.Equal("selfArray", property.Key); + var arraySchema = Assert.IsType(property.Value); + Assert.Equal(JsonSchemaType.Array, arraySchema.Type); + var itemReference = Assert.IsType(arraySchema.Items); + Assert.Equal("#/components/schemas/CircularReferenceWithArrayModel", itemReference.Reference.ReferenceV3); + }); + }); + } + // Test models for issue 61194 private class Config { @@ -1203,5 +1235,23 @@ private class ReferencedModel { public int Id { get; set; } } + + // Test models for issue 64048 + public class CircularReferenceWithArrayRootOrderArrayLast + { + public CircularReferenceWithArrayModel Item { get; set; } + public ICollection ItemArray { get; set; } + } + + public class CircularReferenceWithArrayRootOrderArrayFirst + { + public ICollection ItemArray { get; set; } + public CircularReferenceWithArrayModel Item { get; set; } + } + + public class CircularReferenceWithArrayModel + { + public ICollection SelfArray { get; set; } + } } #nullable restore From 3d1af007e7a5691339bcc10f74b4986002f9976b Mon Sep 17 00:00:00 2001 From: Sjoerd van der Meer Date: Mon, 20 Oct 2025 00:10:08 +0200 Subject: [PATCH 2/5] Fix array circular references. The fix is done by registering components before going deep into the recursion tree (where the leaves would be registered first). Fixing this revealed an issue for default values for "local" attributes. Local attributes/parameter info should not apply to componetized schemas. --- .../Extensions/JsonNodeSchemaExtensions.cs | 7 ++++-- .../Extensions/OpenApiDocumentExtensions.cs | 12 ++++++---- .../src/Schemas/OpenApiJsonSchema.Helpers.cs | 6 ++++- src/OpenApi/src/Services/OpenApiConstants.cs | 1 + .../Services/Schemas/OpenApiSchemaService.cs | 24 +++++++++++++++---- ...OpenApiSchemaService.RequestBodySchemas.cs | 5 ++-- 6 files changed, 40 insertions(+), 15 deletions(-) diff --git a/src/OpenApi/src/Extensions/JsonNodeSchemaExtensions.cs b/src/OpenApi/src/Extensions/JsonNodeSchemaExtensions.cs index 945326b1d5b1..c7caf4d1453a 100644 --- a/src/OpenApi/src/Extensions/JsonNodeSchemaExtensions.cs +++ b/src/OpenApi/src/Extensions/JsonNodeSchemaExtensions.cs @@ -175,13 +175,16 @@ internal static void ApplyDefaultValue(this JsonNode schema, object? defaultValu return; } + var isReferencedSchema = schema[OpenApiConstants.SchemaId] is null; + var schemaAttribute = isReferencedSchema ? OpenApiConstants.RefDefaultAnnotation : OpenApiSchemaKeywords.DefaultKeyword; + if (defaultValue is null) { - schema[OpenApiSchemaKeywords.DefaultKeyword] = null; + schema[schemaAttribute] = null; } else { - schema[OpenApiSchemaKeywords.DefaultKeyword] = JsonSerializer.SerializeToNode(defaultValue, jsonTypeInfo); + schema[schemaAttribute] = JsonSerializer.SerializeToNode(defaultValue, jsonTypeInfo); } } diff --git a/src/OpenApi/src/Extensions/OpenApiDocumentExtensions.cs b/src/OpenApi/src/Extensions/OpenApiDocumentExtensions.cs index c09bd50dc67b..9a73be8e64fb 100644 --- a/src/OpenApi/src/Extensions/OpenApiDocumentExtensions.cs +++ b/src/OpenApi/src/Extensions/OpenApiDocumentExtensions.cs @@ -17,25 +17,27 @@ internal static class OpenApiDocumentExtensions /// An with a reference to the stored schema. public static IOpenApiSchema AddOpenApiSchemaByReference(this OpenApiDocument document, string schemaId, IOpenApiSchema schema) { - document.Components ??= new(); - document.Components.Schemas ??= new Dictionary(); - document.Components.Schemas[schemaId] = schema; + // Make sure the document has a workspace, + // AddComponent will add it to the workspace when adding the component. document.Workspace ??= new(); - var location = document.BaseUri + "/components/schemas/" + schemaId; - document.Workspace.RegisterComponentForDocument(document, schema, location); + // AddComponent will only add the schema if it doesn't already exist. + document.AddComponent(schemaId, schema); object? description = null; object? example = null; + object? defaultAnnotation = null; if (schema is OpenApiSchema actualSchema) { actualSchema.Metadata?.TryGetValue(OpenApiConstants.RefDescriptionAnnotation, out description); actualSchema.Metadata?.TryGetValue(OpenApiConstants.RefExampleAnnotation, out example); + actualSchema.Metadata?.TryGetValue(OpenApiConstants.RefDefaultAnnotation, out defaultAnnotation); } return new OpenApiSchemaReference(schemaId, document) { Description = description as string, Examples = example is JsonNode exampleJson ? [exampleJson] : null, + Default = defaultAnnotation as JsonNode, }; } } diff --git a/src/OpenApi/src/Schemas/OpenApiJsonSchema.Helpers.cs b/src/OpenApi/src/Schemas/OpenApiJsonSchema.Helpers.cs index 877ac70010db..4ba02d1a3eb5 100644 --- a/src/OpenApi/src/Schemas/OpenApiJsonSchema.Helpers.cs +++ b/src/OpenApi/src/Schemas/OpenApiJsonSchema.Helpers.cs @@ -355,7 +355,11 @@ public static void ReadProperty(ref Utf8JsonReader reader, string propertyName, schema.Metadata ??= new Dictionary(); schema.Metadata[OpenApiConstants.RefDescriptionAnnotation] = reader.GetString() ?? string.Empty; break; - + case OpenApiConstants.RefDefaultAnnotation: + reader.Read(); + schema.Metadata ??= new Dictionary(); + schema.Metadata[OpenApiConstants.RefDefaultAnnotation] = ReadJsonNode(ref reader)!; + break; default: reader.Skip(); break; diff --git a/src/OpenApi/src/Services/OpenApiConstants.cs b/src/OpenApi/src/Services/OpenApiConstants.cs index df4228633556..433e71573eb9 100644 --- a/src/OpenApi/src/Services/OpenApiConstants.cs +++ b/src/OpenApi/src/Services/OpenApiConstants.cs @@ -13,6 +13,7 @@ internal static class OpenApiConstants internal const string DescriptionId = "x-aspnetcore-id"; internal const string SchemaId = "x-schema-id"; internal const string RefId = "x-ref-id"; + internal const string RefDefaultAnnotation = "x-ref-default"; internal const string RefDescriptionAnnotation = "x-ref-description"; internal const string RefExampleAnnotation = "x-ref-example"; internal const string RefKeyword = "$ref"; diff --git a/src/OpenApi/src/Services/Schemas/OpenApiSchemaService.cs b/src/OpenApi/src/Services/Schemas/OpenApiSchemaService.cs index 44bf7dbd3da5..e6018ff7ce02 100644 --- a/src/OpenApi/src/Services/Schemas/OpenApiSchemaService.cs +++ b/src/OpenApi/src/Services/Schemas/OpenApiSchemaService.cs @@ -265,6 +265,20 @@ internal static IOpenApiSchema ResolveReferenceForSchema(OpenApiDocument documen { var schema = UnwrapOpenApiSchema(inputSchema); + if (inputSchema is OpenApiSchema && schema.Metadata is not null && + !schema.Metadata.ContainsKey(OpenApiConstants.RefId) && + schema.Metadata.TryGetValue(OpenApiConstants.SchemaId, out var referenceId) && + referenceId is string referenceIdString) + { + var targetReferenceId = baseSchemaId is not null + ? $"{baseSchemaId}{referenceIdString}" + : referenceIdString; + if (!string.IsNullOrEmpty(targetReferenceId)) + { + document.AddOpenApiSchemaByReference(targetReferenceId, schema); + } + } + if (schema.Metadata is not null && schema.Metadata.TryGetValue(OpenApiConstants.SchemaId, out var resolvedBaseSchemaId)) { @@ -347,14 +361,14 @@ internal static IOpenApiSchema ResolveReferenceForSchema(OpenApiDocument documen // we don't want to replace the top-level inline schema with a reference to itself. We want to replace // inline schemas to reference schemas for all schemas referenced in the top-level schema though (such as // `allOf`, `oneOf`, `anyOf`, `items`, `properties`, etc.) which is why `isTopLevel` is only set once. - if (schema is OpenApiSchema && schema.Metadata is not null && + if (inputSchema is OpenApiSchema && schema.Metadata is not null && !schema.Metadata.ContainsKey(OpenApiConstants.RefId) && - schema.Metadata.TryGetValue(OpenApiConstants.SchemaId, out var referenceId) && - referenceId is string referenceIdString) + schema.Metadata.TryGetValue(OpenApiConstants.SchemaId, out var referenceId2) && + referenceId2 is string referenceIdString2) { var targetReferenceId = baseSchemaId is not null - ? $"{baseSchemaId}{referenceIdString}" - : referenceIdString; + ? $"{baseSchemaId}{referenceIdString2}" + : referenceIdString2; if (!string.IsNullOrEmpty(targetReferenceId)) { return document.AddOpenApiSchemaByReference(targetReferenceId, schema); diff --git a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Services/OpenApiSchemaService/OpenApiSchemaService.RequestBodySchemas.cs b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Services/OpenApiSchemaService/OpenApiSchemaService.RequestBodySchemas.cs index 2c368e559c49..99ffe0ca3ddd 100644 --- a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Services/OpenApiSchemaService/OpenApiSchemaService.RequestBodySchemas.cs +++ b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Services/OpenApiSchemaService/OpenApiSchemaService.RequestBodySchemas.cs @@ -984,8 +984,9 @@ private void VerifyOptionalEnum(OpenApiDocument document) var property = properties["status"]; Assert.NotNull(property); - Assert.Equal(3, property.Enum.Count); - Assert.Equal("Approved", property.Default.GetValue()); + var statusReference = Assert.IsType(property); + Assert.Equal(3, statusReference.RecursiveTarget.Enum.Count); + Assert.Equal("Approved", statusReference.Default.GetValue()); } [ApiController] From 66a8a1a48dfd73101cb7681fca968414fff55446 Mon Sep 17 00:00:00 2001 From: Sjoerd van der Meer Date: Mon, 20 Oct 2025 12:18:45 +0200 Subject: [PATCH 3/5] Fix build warnings --- .../OpenApiSchemaReferenceTransformerTests.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Transformers/Implementations/OpenApiSchemaReferenceTransformerTests.cs b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Transformers/Implementations/OpenApiSchemaReferenceTransformerTests.cs index 8c1ca9d06a6f..55118b879f66 100644 --- a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Transformers/Implementations/OpenApiSchemaReferenceTransformerTests.cs +++ b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Transformers/Implementations/OpenApiSchemaReferenceTransformerTests.cs @@ -1239,19 +1239,19 @@ private class ReferencedModel // Test models for issue 64048 public class CircularReferenceWithArrayRootOrderArrayLast { - public CircularReferenceWithArrayModel Item { get; set; } - public ICollection ItemArray { get; set; } + public CircularReferenceWithArrayModel Item { get; set; } = null!; + public ICollection ItemArray { get; set; } = []; } public class CircularReferenceWithArrayRootOrderArrayFirst { - public ICollection ItemArray { get; set; } - public CircularReferenceWithArrayModel Item { get; set; } + public ICollection ItemArray { get; set; } = []; + public CircularReferenceWithArrayModel Item { get; set; } = null!; } public class CircularReferenceWithArrayModel { - public ICollection SelfArray { get; set; } + public ICollection SelfArray { get; set; } = []; } } #nullable restore From 71dfe861009b1d5003c90709eeeb42a04397e1fd Mon Sep 17 00:00:00 2001 From: Sjoerd van der Meer Date: Mon, 20 Oct 2025 14:25:16 +0200 Subject: [PATCH 4/5] Fix missing negation --- src/OpenApi/src/Extensions/JsonNodeSchemaExtensions.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/OpenApi/src/Extensions/JsonNodeSchemaExtensions.cs b/src/OpenApi/src/Extensions/JsonNodeSchemaExtensions.cs index c7caf4d1453a..2f7a9884d90c 100644 --- a/src/OpenApi/src/Extensions/JsonNodeSchemaExtensions.cs +++ b/src/OpenApi/src/Extensions/JsonNodeSchemaExtensions.cs @@ -175,7 +175,7 @@ internal static void ApplyDefaultValue(this JsonNode schema, object? defaultValu return; } - var isReferencedSchema = schema[OpenApiConstants.SchemaId] is null; + var isReferencedSchema = schema[OpenApiConstants.SchemaId] is not null; var schemaAttribute = isReferencedSchema ? OpenApiConstants.RefDefaultAnnotation : OpenApiSchemaKeywords.DefaultKeyword; if (defaultValue is null) From 32a3dd18a3123f184953a167b1a850381b9db833 Mon Sep 17 00:00:00 2001 From: Sjoerd van der Meer Date: Mon, 20 Oct 2025 23:11:40 +0200 Subject: [PATCH 5/5] Cleanup some funky logic --- .../Extensions/JsonNodeSchemaExtensions.cs | 37 +++++++++-- .../src/Extensions/OpenApiSchemaExtensions.cs | 40 ++++++++++++ .../Services/Schemas/OpenApiSchemaService.cs | 61 +++++-------------- 3 files changed, 87 insertions(+), 51 deletions(-) diff --git a/src/OpenApi/src/Extensions/JsonNodeSchemaExtensions.cs b/src/OpenApi/src/Extensions/JsonNodeSchemaExtensions.cs index 2f7a9884d90c..c1036ffad128 100644 --- a/src/OpenApi/src/Extensions/JsonNodeSchemaExtensions.cs +++ b/src/OpenApi/src/Extensions/JsonNodeSchemaExtensions.cs @@ -3,6 +3,7 @@ using System.ComponentModel; using System.ComponentModel.DataAnnotations; +using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.Linq; using System.Reflection; @@ -175,8 +176,9 @@ internal static void ApplyDefaultValue(this JsonNode schema, object? defaultValu return; } - var isReferencedSchema = schema[OpenApiConstants.SchemaId] is not null; - var schemaAttribute = isReferencedSchema ? OpenApiConstants.RefDefaultAnnotation : OpenApiSchemaKeywords.DefaultKeyword; + var schemaAttribute = schema.WillBeComponentized() + ? OpenApiConstants.RefDefaultAnnotation + : OpenApiSchemaKeywords.DefaultKeyword; if (defaultValue is null) { @@ -432,6 +434,33 @@ internal static void ApplySchemaReferenceId(this JsonNode schema, JsonSchemaExpo } } + /// + /// Determines whether the specified JSON schema will be moved into the components section. + /// + /// The produced by the underlying schema generator. + /// if the schema will be componentized; otherwise, . + internal static bool WillBeComponentized(this JsonNode schema) + => schema.WillBeComponentized(out _); + + /// + /// Determines whether the specified JSON schema node contains a componentized schema identifier. + /// + /// The JSON schema node to inspect for a componentized schema identifier. + /// When this method returns , contains the schema identifier found in the node; otherwise, + /// . + /// if the schema will be componentized; otherwise, . + internal static bool WillBeComponentized(this JsonNode schema, [NotNullWhen(true)] out string? schemaId) + { + if (schema[OpenApiConstants.SchemaId] is JsonNode schemaIdNode + && schemaIdNode.GetValueKind() == JsonValueKind.String) + { + schemaId = schemaIdNode.GetValue(); + return true; + } + schemaId = null; + return false; + } + /// /// Returns if the current type is a non-abstract base class that is not defined as its /// own derived type. @@ -461,7 +490,7 @@ internal static void ApplyNullabilityContextInfo(this JsonNode schema, JsonPrope schema[OpenApiSchemaKeywords.TypeKeyword] = (schemaTypes | JsonSchemaType.Null).ToString(); } } - if (schema[OpenApiConstants.SchemaId] is not null && + if (schema.WillBeComponentized() && propertyInfo.PropertyType != typeof(object) && propertyInfo.ShouldApplyNullablePropertySchema()) { schema[OpenApiConstants.NullableProperty] = true; @@ -475,7 +504,7 @@ internal static void ApplyNullabilityContextInfo(this JsonNode schema, JsonPrope /// The produced by the underlying schema generator. internal static void PruneNullTypeForComponentizedTypes(this JsonNode schema) { - if (schema[OpenApiConstants.SchemaId] is not null && + if (schema.WillBeComponentized() && schema[OpenApiSchemaKeywords.TypeKeyword] is JsonArray typeArray) { for (var i = typeArray.Count - 1; i >= 0; i--) diff --git a/src/OpenApi/src/Extensions/OpenApiSchemaExtensions.cs b/src/OpenApi/src/Extensions/OpenApiSchemaExtensions.cs index f394445850fe..4864a835f4a8 100644 --- a/src/OpenApi/src/Extensions/OpenApiSchemaExtensions.cs +++ b/src/OpenApi/src/Extensions/OpenApiSchemaExtensions.cs @@ -1,6 +1,8 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Text.Json.Nodes; + namespace Microsoft.AspNetCore.OpenApi; internal static class OpenApiSchemaExtensions @@ -18,4 +20,42 @@ public static IOpenApiSchema CreateOneOfNullableWrapper(this IOpenApiSchema orig ] }; } + + public static bool IsComponentizedSchema(this OpenApiSchema schema) + => schema.IsComponentizedSchema(out _); + + public static bool IsComponentizedSchema(this OpenApiSchema schema, out string schemaId) + { + if(schema.Metadata is not null + && schema.Metadata.TryGetValue(OpenApiConstants.SchemaId, out var schemaIdAsObject) + && schemaIdAsObject is string schemaIdString) + { + schemaId = schemaIdString; + return true; + } + schemaId = string.Empty; + return false; + } + + public static OpenApiSchemaReference CreateReference(this OpenApiSchema schema, OpenApiDocument document) + { + if (!schema.IsComponentizedSchema(out var schemaId)) + { + throw new InvalidOperationException("Schema is not a componentized schema."); + } + + object? description = null; + object? example = null; + object? defaultAnnotation = null; + schema.Metadata?.TryGetValue(OpenApiConstants.RefDescriptionAnnotation, out description); + schema.Metadata?.TryGetValue(OpenApiConstants.RefExampleAnnotation, out example); + schema.Metadata?.TryGetValue(OpenApiConstants.RefDefaultAnnotation, out defaultAnnotation); + + return new OpenApiSchemaReference(schemaId, document) + { + Description = description as string, + Examples = example is JsonNode exampleJson ? [exampleJson] : null, + Default = defaultAnnotation as JsonNode, + }; + } } diff --git a/src/OpenApi/src/Services/Schemas/OpenApiSchemaService.cs b/src/OpenApi/src/Services/Schemas/OpenApiSchemaService.cs index e6018ff7ce02..fe63cba3767a 100644 --- a/src/OpenApi/src/Services/Schemas/OpenApiSchemaService.cs +++ b/src/OpenApi/src/Services/Schemas/OpenApiSchemaService.cs @@ -116,7 +116,7 @@ internal sealed class OpenApiSchemaService( { schema.ApplyDefaultValue(defaultValueAttribute.Value, context.TypeInfo); } - var isInlinedSchema = schema[OpenApiConstants.SchemaId] is null; + var isInlinedSchema = !schema.WillBeComponentized(); if (isInlinedSchema) { if (propertyAttributes.OfType().LastOrDefault() is { } descriptionAttribute) @@ -265,29 +265,26 @@ internal static IOpenApiSchema ResolveReferenceForSchema(OpenApiDocument documen { var schema = UnwrapOpenApiSchema(inputSchema); - if (inputSchema is OpenApiSchema && schema.Metadata is not null && - !schema.Metadata.ContainsKey(OpenApiConstants.RefId) && - schema.Metadata.TryGetValue(OpenApiConstants.SchemaId, out var referenceId) && - referenceId is string referenceIdString) + var isComponentizedSchema = schema.IsComponentizedSchema(out var schemaId); + + // When we register it, this will be the resulting reference + IOpenApiSchema? resultSchemaReference = null; + if (inputSchema is OpenApiSchema && isComponentizedSchema) { var targetReferenceId = baseSchemaId is not null - ? $"{baseSchemaId}{referenceIdString}" - : referenceIdString; + ? $"{baseSchemaId}{schemaId}" + : schemaId; if (!string.IsNullOrEmpty(targetReferenceId)) { - document.AddOpenApiSchemaByReference(targetReferenceId, schema); + resultSchemaReference = document.AddOpenApiSchemaByReference(targetReferenceId, schema); } } - if (schema.Metadata is not null && - schema.Metadata.TryGetValue(OpenApiConstants.SchemaId, out var resolvedBaseSchemaId)) + if (schema.AnyOf is { Count: > 0 }) { - if (schema.AnyOf is { Count: > 0 }) + for (var i = 0; i < schema.AnyOf.Count; i++) { - for (var i = 0; i < schema.AnyOf.Count; i++) - { - schema.AnyOf[i] = ResolveReferenceForSchema(document, schema.AnyOf[i], rootSchemaId, resolvedBaseSchemaId?.ToString()); - } + schema.AnyOf[i] = ResolveReferenceForSchema(document, schema.AnyOf[i], rootSchemaId, schemaId); } } @@ -340,39 +337,9 @@ internal static IOpenApiSchema ResolveReferenceForSchema(OpenApiDocument documen schema.Not = ResolveReferenceForSchema(document, schema.Not, rootSchemaId); } - // Handle schemas where the references have been inlined by the JsonSchemaExporter. In this case, - // the `#` ID is generated by the exporter since it has no base document to baseline against. In this - // case we we want to replace the reference ID with the schema ID that was generated by the - // `CreateSchemaReferenceId` method in the OpenApiSchemaService. - if (schema.Metadata is not null && - schema.Metadata.TryGetValue(OpenApiConstants.RefId, out var refId) && - refId is string refIdString) - { - if (schema.Metadata.TryGetValue(OpenApiConstants.SchemaId, out var schemaId) && - schemaId is string schemaIdString) - { - return new OpenApiSchemaReference(schemaIdString, document); - } - var relativeSchemaId = $"#/components/schemas/{rootSchemaId}{refIdString.Replace("#", string.Empty)}"; - return new OpenApiSchemaReference(relativeSchemaId, document); - } - - // If we're resolving schemas for a top-level schema being referenced in the `components.schema` property - // we don't want to replace the top-level inline schema with a reference to itself. We want to replace - // inline schemas to reference schemas for all schemas referenced in the top-level schema though (such as - // `allOf`, `oneOf`, `anyOf`, `items`, `properties`, etc.) which is why `isTopLevel` is only set once. - if (inputSchema is OpenApiSchema && schema.Metadata is not null && - !schema.Metadata.ContainsKey(OpenApiConstants.RefId) && - schema.Metadata.TryGetValue(OpenApiConstants.SchemaId, out var referenceId2) && - referenceId2 is string referenceIdString2) + if (resultSchemaReference is not null) { - var targetReferenceId = baseSchemaId is not null - ? $"{baseSchemaId}{referenceIdString2}" - : referenceIdString2; - if (!string.IsNullOrEmpty(targetReferenceId)) - { - return document.AddOpenApiSchemaByReference(targetReferenceId, schema); - } + return resultSchemaReference; } return schema;