From b75709adc868fa11e869583ab469537ec2dc4f67 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 26 May 2026 10:09:07 +0000 Subject: [PATCH 1/3] Initial plan From 94b606dae1ab89d291133e986d57cf944f787945 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 26 May 2026 10:16:07 +0000 Subject: [PATCH 2/3] fix(reader): preserve nullable Null flag when type appears after nullable in V3.1/V3.2 deserializers Agent-Logs-Url: https://github.com/microsoft/OpenAPI.NET/sessions/40596ef1-e9f2-4d1b-b9e2-0650b96d73b4 Co-authored-by: baywet <7905502+baywet@users.noreply.github.com> --- .../Reader/V31/OpenApiSchemaDeserializer.cs | 7 +++++-- .../Reader/V32/OpenApiSchemaDeserializer.cs | 7 +++++-- .../V31Tests/OpenApiSchemaTests.cs | 12 ++++++++++++ 3 files changed, 22 insertions(+), 4 deletions(-) diff --git a/src/Microsoft.OpenApi/Reader/V31/OpenApiSchemaDeserializer.cs b/src/Microsoft.OpenApi/Reader/V31/OpenApiSchemaDeserializer.cs index 39c32c979..296626a93 100644 --- a/src/Microsoft.OpenApi/Reader/V31/OpenApiSchemaDeserializer.cs +++ b/src/Microsoft.OpenApi/Reader/V31/OpenApiSchemaDeserializer.cs @@ -198,14 +198,17 @@ internal static partial class OpenApiV31Deserializer "type", (o, n, doc) => { + // Preserve any Null flag set by a preceding "nullable: true" handler + var preserveNull = o.Type.HasValue && o.Type.Value.HasFlag(JsonSchemaType.Null); if (n is ValueNode) { - o.Type = n.GetScalarValue()?.ToJsonSchemaType(); + var parsedType = n.GetScalarValue()?.ToJsonSchemaType(); + o.Type = preserveNull ? parsedType | JsonSchemaType.Null : parsedType; } else { var list = n.CreateSimpleList((n2, p) => n2.GetScalarValue(), doc); - JsonSchemaType combinedType = 0; + JsonSchemaType combinedType = preserveNull ? JsonSchemaType.Null : 0; foreach(var type in list.Where(static t => t is not null).Select(static t => t!.ToJsonSchemaType())) { combinedType |= type; diff --git a/src/Microsoft.OpenApi/Reader/V32/OpenApiSchemaDeserializer.cs b/src/Microsoft.OpenApi/Reader/V32/OpenApiSchemaDeserializer.cs index c18c48462..f0df47b99 100644 --- a/src/Microsoft.OpenApi/Reader/V32/OpenApiSchemaDeserializer.cs +++ b/src/Microsoft.OpenApi/Reader/V32/OpenApiSchemaDeserializer.cs @@ -198,14 +198,17 @@ internal static partial class OpenApiV32Deserializer "type", (o, n, doc) => { + // Preserve any Null flag set by a preceding "nullable: true" handler + var preserveNull = o.Type.HasValue && o.Type.Value.HasFlag(JsonSchemaType.Null); if (n is ValueNode) { - o.Type = n.GetScalarValue()?.ToJsonSchemaType(); + var parsedType = n.GetScalarValue()?.ToJsonSchemaType(); + o.Type = preserveNull ? parsedType | JsonSchemaType.Null : parsedType; } else { var list = n.CreateSimpleList((n2, p) => n2.GetScalarValue(), doc); - JsonSchemaType combinedType = 0; + JsonSchemaType combinedType = preserveNull ? JsonSchemaType.Null : 0; foreach(var type in list.Where(static t => t is not null).Select(static t => t!.ToJsonSchemaType())) { combinedType |= type; diff --git a/test/Microsoft.OpenApi.Readers.Tests/V31Tests/OpenApiSchemaTests.cs b/test/Microsoft.OpenApi.Readers.Tests/V31Tests/OpenApiSchemaTests.cs index b65b2d8c3..ec66dcbb9 100644 --- a/test/Microsoft.OpenApi.Readers.Tests/V31Tests/OpenApiSchemaTests.cs +++ b/test/Microsoft.OpenApi.Readers.Tests/V31Tests/OpenApiSchemaTests.cs @@ -132,6 +132,18 @@ public void ParseSchemaWithTypeArrayWorks() Assert.Equivalent(expected, actual); } + [Theory] + [InlineData(@"{ ""nullable"": true, ""type"": ""string"" }")] + [InlineData(@"{ ""type"": ""string"", ""nullable"": true }")] + public void ParseSchemaWithNullableBeforeOrAfterTypePreservesNullFlag(string schemaJson) + { + // Act + var schema = OpenApiModelFactory.Parse(schemaJson, OpenApiSpecVersion.OpenApi3_1, new(), out _, "json", SettingsFixture.ReaderSettings); + + // Assert + Assert.Equal(JsonSchemaType.String | JsonSchemaType.Null, schema.Type); + } + [Fact] public void TestSchemaCopyConstructorWithTypeArrayWorks() { From de72b1dd2ad152152bd36d6713fffd8b8670c1fd Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 26 May 2026 12:21:07 +0000 Subject: [PATCH 3/3] test(reader): add nullable-before-type tests for V3.0 and V3.2 Agent-Logs-Url: https://github.com/microsoft/OpenAPI.NET/sessions/6de5cea9-ff8f-4a95-804d-493007f279f3 Co-authored-by: baywet <7905502+baywet@users.noreply.github.com> --- .../V32Tests/OpenApiSchemaTests.cs | 12 ++++++++++++ .../V3Tests/OpenApiSchemaTests.cs | 12 ++++++++++++ 2 files changed, 24 insertions(+) diff --git a/test/Microsoft.OpenApi.Readers.Tests/V32Tests/OpenApiSchemaTests.cs b/test/Microsoft.OpenApi.Readers.Tests/V32Tests/OpenApiSchemaTests.cs index 7f91b0327..621cd156c 100644 --- a/test/Microsoft.OpenApi.Readers.Tests/V32Tests/OpenApiSchemaTests.cs +++ b/test/Microsoft.OpenApi.Readers.Tests/V32Tests/OpenApiSchemaTests.cs @@ -131,6 +131,18 @@ public void ParseSchemaWithTypeArrayWorks() Assert.Equivalent(expected, actual); } + [Theory] + [InlineData(@"{ ""nullable"": true, ""type"": ""string"" }")] + [InlineData(@"{ ""type"": ""string"", ""nullable"": true }")] + public void ParseSchemaWithNullableBeforeOrAfterTypePreservesNullFlag(string schemaJson) + { + // Act + var schema = OpenApiModelFactory.Parse(schemaJson, OpenApiSpecVersion.OpenApi3_2, new(), out _, "json", SettingsFixture.ReaderSettings); + + // Assert + Assert.Equal(JsonSchemaType.String | JsonSchemaType.Null, schema.Type); + } + [Fact] public void TestSchemaCopyConstructorWithTypeArrayWorks() { diff --git a/test/Microsoft.OpenApi.Readers.Tests/V3Tests/OpenApiSchemaTests.cs b/test/Microsoft.OpenApi.Readers.Tests/V3Tests/OpenApiSchemaTests.cs index e16e40594..8241e55ed 100644 --- a/test/Microsoft.OpenApi.Readers.Tests/V3Tests/OpenApiSchemaTests.cs +++ b/test/Microsoft.OpenApi.Readers.Tests/V3Tests/OpenApiSchemaTests.cs @@ -133,6 +133,18 @@ public void ParsePathFragmentShouldSucceed() }, openApiAny); } + [Theory] + [InlineData(@"{ ""nullable"": true, ""type"": ""string"" }")] + [InlineData(@"{ ""type"": ""string"", ""nullable"": true }")] + public void ParseSchemaWithNullableBeforeOrAfterTypePreservesNullFlag(string schemaJson) + { + // Act + var schema = OpenApiModelFactory.Parse(schemaJson, OpenApiSpecVersion.OpenApi3_0, new(), out _, "json", SettingsFixture.ReaderSettings); + + // Assert + Assert.Equal(JsonSchemaType.String | JsonSchemaType.Null, schema.Type); + } + [Fact] public void ParseDictionarySchemaShouldSucceed() {