From 8cc7d4ed8e49f67a351f2358256455750f8bbb3b Mon Sep 17 00:00:00 2001 From: Maggiekimani1 Date: Thu, 20 Feb 2025 18:39:57 +0300 Subject: [PATCH 1/6] fix: remove type casting for strings --- .../Writers/OpenApiWriterAnyExtensions.cs | 51 ++----------------- .../V31Tests/OpenApiSchemaTests.cs | 36 +++++++++++++ .../OpenApiWriterAnyExtensionsTests.cs | 8 +-- 3 files changed, 43 insertions(+), 52 deletions(-) diff --git a/src/Microsoft.OpenApi/Writers/OpenApiWriterAnyExtensions.cs b/src/Microsoft.OpenApi/Writers/OpenApiWriterAnyExtensions.cs index fd2ff1387..639d42ef6 100644 --- a/src/Microsoft.OpenApi/Writers/OpenApiWriterAnyExtensions.cs +++ b/src/Microsoft.OpenApi/Writers/OpenApiWriterAnyExtensions.cs @@ -68,13 +68,13 @@ public static void WriteAny(this IOpenApiWriter writer, JsonNode node) writer.WriteObject(node as JsonObject); break; case JsonValueKind.String: // Primitive - writer.WritePrimitive(node); + writer.WriteValue(node.GetValue()); break; case JsonValueKind.Number: // Primitive - writer.WritePrimitive(node); + writer.WriteNumber(node); break; case JsonValueKind.True or JsonValueKind.False: // Primitive - writer.WritePrimitive(node); + writer.WriteValue(node.GetValue()); break; case JsonValueKind.Null: // null writer.WriteNull(); @@ -109,43 +109,10 @@ private static void WriteObject(this IOpenApiWriter writer, JsonObject entity) writer.WriteEndObject(); } - private static void WritePrimitive(this IOpenApiWriter writer, JsonNode primitive) + private static void WriteNumber(this IOpenApiWriter writer, JsonNode number) { - Utils.CheckArgumentNull(writer); - - var valueKind = primitive.GetValueKind(); - - if (valueKind == JsonValueKind.String && primitive is JsonValue jsonStrValue) + if (number is JsonValue jsonValue) { - if (jsonStrValue.TryGetValue(out var dto)) - { - writer.WriteValue(dto); - } - else if (jsonStrValue.TryGetValue(out var dt)) - { - writer.WriteValue(dt); - } - else if (jsonStrValue.TryGetValue(out var strValue)) - { - // check whether string is actual string or date time object - if (DateTimeOffset.TryParse(strValue, CultureInfo.InvariantCulture, DateTimeStyles.RoundtripKind, out var dateTimeOffset)) - { - writer.WriteValue(dateTimeOffset); - } - else if (DateTime.TryParse(strValue, CultureInfo.InvariantCulture, DateTimeStyles.RoundtripKind, out var dateTime)) - { // order matters, DTO needs to be checked first!!! - writer.WriteValue(dateTime); - } - else - { - writer.WriteValue(strValue); - } - } - } - - else if (valueKind == JsonValueKind.Number && primitive is JsonValue jsonValue) - { - if (jsonValue.TryGetValue(out var decimalValue)) { writer.WriteValue(decimalValue); @@ -167,14 +134,6 @@ private static void WritePrimitive(this IOpenApiWriter writer, JsonNode primitiv writer.WriteValue(intValue); } } - else if (valueKind is JsonValueKind.False) - { - writer.WriteValue(false); - } - else if (valueKind is JsonValueKind.True) - { - writer.WriteValue(true); - } } } } diff --git a/test/Microsoft.OpenApi.Readers.Tests/V31Tests/OpenApiSchemaTests.cs b/test/Microsoft.OpenApi.Readers.Tests/V31Tests/OpenApiSchemaTests.cs index 555b71c54..e220df055 100644 --- a/test/Microsoft.OpenApi.Readers.Tests/V31Tests/OpenApiSchemaTests.cs +++ b/test/Microsoft.OpenApi.Readers.Tests/V31Tests/OpenApiSchemaTests.cs @@ -520,5 +520,41 @@ public void ParseSchemaWithUnrecognizedKeywordsWorks() Assert.Equal(2, schema.UnrecognizedKeywords.Count); } + [Fact] + public void ParseSchemaExampleWithPrimitivesWorks() + { + var expected1 = @"{ + ""type"": ""string"", + ""example"": ""2024-01-02"" +}"; + + var expected2 = @"{ + ""type"": ""string"", + ""example"": ""3.14"" +}"; + var schema = new OpenApiSchema() + { + Type = JsonSchemaType.String, + Example = JsonValue.Create("2024-01-02") + }; + + var schema2 = new OpenApiSchema() + { + Type = JsonSchemaType.String, + Example = JsonValue.Create("3.14") + }; + + var textWriter = new StringWriter(); + var writer = new OpenApiJsonWriter(textWriter); + schema.SerializeAsV31(writer); + var actual1 = textWriter.ToString(); + Assert.Equal(expected1.MakeLineBreaksEnvironmentNeutral(), actual1.MakeLineBreaksEnvironmentNeutral()); + + textWriter = new StringWriter(); + writer = new OpenApiJsonWriter(textWriter); + schema2.SerializeAsV31(writer); + var actual2 = textWriter.ToString(); + Assert.Equal(expected2.MakeLineBreaksEnvironmentNeutral(), actual2.MakeLineBreaksEnvironmentNeutral()); + } } } diff --git a/test/Microsoft.OpenApi.Tests/Writers/OpenApiWriterAnyExtensionsTests.cs b/test/Microsoft.OpenApi.Tests/Writers/OpenApiWriterAnyExtensionsTests.cs index 2e05a70a3..149797e14 100644 --- a/test/Microsoft.OpenApi.Tests/Writers/OpenApiWriterAnyExtensionsTests.cs +++ b/test/Microsoft.OpenApi.Tests/Writers/OpenApiWriterAnyExtensionsTests.cs @@ -164,12 +164,8 @@ from shouldBeTerse in shouldProduceTerseOutputValues [MemberData(nameof(StringifiedDateTimes))] public async Task WriteOpenApiDateTimeAsJsonWorksAsync(string inputString, bool produceTerseOutput) { - // Arrange - var input = DateTimeOffset.Parse(inputString, CultureInfo.InvariantCulture); - var dateTimeValue = input; - - var json = await WriteAsJsonAsync(dateTimeValue, produceTerseOutput); - var expectedJson = "\"" + input.ToString("o") + "\""; + var json = await WriteAsJsonAsync(inputString, produceTerseOutput); + var expectedJson = "\"" + inputString + "\""; // Assert Assert.Equal(expectedJson, json); From 9ed37d2411ef09943c7565c44a55e319ef343e09 Mon Sep 17 00:00:00 2001 From: Maggiekimani1 Date: Mon, 24 Feb 2025 15:45:43 +0300 Subject: [PATCH 2/6] chore: code cleanup --- .../Writers/OpenApiWriterAnyExtensions.cs | 48 ++++++++----------- .../OpenApiWriterAnyExtensionsTests.cs | 5 +- 2 files changed, 23 insertions(+), 30 deletions(-) diff --git a/src/Microsoft.OpenApi/Writers/OpenApiWriterAnyExtensions.cs b/src/Microsoft.OpenApi/Writers/OpenApiWriterAnyExtensions.cs index 639d42ef6..a52d4752f 100644 --- a/src/Microsoft.OpenApi/Writers/OpenApiWriterAnyExtensions.cs +++ b/src/Microsoft.OpenApi/Writers/OpenApiWriterAnyExtensions.cs @@ -1,9 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. -using System; using System.Collections.Generic; -using System.Globalization; using System.Text.Json; using System.Text.Json.Nodes; using Microsoft.OpenApi.Any; @@ -68,13 +66,13 @@ public static void WriteAny(this IOpenApiWriter writer, JsonNode node) writer.WriteObject(node as JsonObject); break; case JsonValueKind.String: // Primitive - writer.WriteValue(node.GetValue()); + writer.WritePrimitive(node.AsValue()); break; case JsonValueKind.Number: // Primitive - writer.WriteNumber(node); + writer.WritePrimitive(node.AsValue()); break; case JsonValueKind.True or JsonValueKind.False: // Primitive - writer.WriteValue(node.GetValue()); + writer.WritePrimitive(node.AsValue()); break; case JsonValueKind.Null: // null writer.WriteNull(); @@ -109,31 +107,23 @@ private static void WriteObject(this IOpenApiWriter writer, JsonObject entity) writer.WriteEndObject(); } - private static void WriteNumber(this IOpenApiWriter writer, JsonNode number) + private static void WritePrimitive(this IOpenApiWriter writer, JsonValue jsonValue) { - if (number is JsonValue jsonValue) - { - if (jsonValue.TryGetValue(out var decimalValue)) - { - writer.WriteValue(decimalValue); - } - else if (jsonValue.TryGetValue(out var doubleValue)) - { - writer.WriteValue(doubleValue); - } - else if (jsonValue.TryGetValue(out var floatValue)) - { - writer.WriteValue(floatValue); - } - else if (jsonValue.TryGetValue(out var longValue)) - { - writer.WriteValue(longValue); - } - else if (jsonValue.TryGetValue(out var intValue)) - { - writer.WriteValue(intValue); - } - } + if (jsonValue.TryGetValue(out string stringValue)) + writer.WriteValue(stringValue); + else if (jsonValue.TryGetValue(out bool boolValue)) + writer.WriteValue(boolValue); + // write number values + else if (jsonValue.TryGetValue(out decimal decimalValue)) + writer.WriteValue(decimalValue); + else if (jsonValue.TryGetValue(out double doubleValue)) + writer.WriteValue(doubleValue); + else if (jsonValue.TryGetValue(out float floatValue)) + writer.WriteValue(floatValue); + else if (jsonValue.TryGetValue(out long longValue)) + writer.WriteValue(longValue); + else if (jsonValue.TryGetValue(out int intValue)) + writer.WriteValue(intValue); } } } diff --git a/test/Microsoft.OpenApi.Tests/Writers/OpenApiWriterAnyExtensionsTests.cs b/test/Microsoft.OpenApi.Tests/Writers/OpenApiWriterAnyExtensionsTests.cs index 149797e14..32ec6bab6 100644 --- a/test/Microsoft.OpenApi.Tests/Writers/OpenApiWriterAnyExtensionsTests.cs +++ b/test/Microsoft.OpenApi.Tests/Writers/OpenApiWriterAnyExtensionsTests.cs @@ -164,7 +164,10 @@ from shouldBeTerse in shouldProduceTerseOutputValues [MemberData(nameof(StringifiedDateTimes))] public async Task WriteOpenApiDateTimeAsJsonWorksAsync(string inputString, bool produceTerseOutput) { - var json = await WriteAsJsonAsync(inputString, produceTerseOutput); + // Arrange + var dateTimeValue = JsonValue.Create(inputString); + + var json = await WriteAsJsonAsync(dateTimeValue, produceTerseOutput); var expectedJson = "\"" + inputString + "\""; // Assert From 23395c5776a781f64a7dc7bfd2867ca83eaa0bb7 Mon Sep 17 00:00:00 2001 From: Maggiekimani1 Date: Mon, 24 Feb 2025 17:37:01 +0300 Subject: [PATCH 3/6] fix: add logic for serializing date time objects --- src/Microsoft.OpenApi/Writers/OpenApiWriterAnyExtensions.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/Microsoft.OpenApi/Writers/OpenApiWriterAnyExtensions.cs b/src/Microsoft.OpenApi/Writers/OpenApiWriterAnyExtensions.cs index a52d4752f..2601a4393 100644 --- a/src/Microsoft.OpenApi/Writers/OpenApiWriterAnyExtensions.cs +++ b/src/Microsoft.OpenApi/Writers/OpenApiWriterAnyExtensions.cs @@ -1,7 +1,9 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. +using System; using System.Collections.Generic; +using System.Globalization; using System.Text.Json; using System.Text.Json.Nodes; using Microsoft.OpenApi.Any; @@ -111,6 +113,10 @@ private static void WritePrimitive(this IOpenApiWriter writer, JsonValue jsonVal { if (jsonValue.TryGetValue(out string stringValue)) writer.WriteValue(stringValue); + else if (jsonValue.TryGetValue(out DateTime dateTimeValue)) + writer.WriteValue(dateTimeValue.ToString("o", CultureInfo.InvariantCulture)); // ISO 8601 format + else if (jsonValue.TryGetValue(out DateTimeOffset dateTimeOffsetValue)) + writer.WriteValue(dateTimeOffsetValue.ToString("o", CultureInfo.InvariantCulture)); else if (jsonValue.TryGetValue(out bool boolValue)) writer.WriteValue(boolValue); // write number values From e133de07ec8b91d4ddc551ba7649a284ad37b2ca Mon Sep 17 00:00:00 2001 From: Maggiekimani1 Date: Mon, 24 Feb 2025 17:37:33 +0300 Subject: [PATCH 4/6] chore: revert change to validate date time serialization --- .../Writers/OpenApiWriterAnyExtensionsTests.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/Microsoft.OpenApi.Tests/Writers/OpenApiWriterAnyExtensionsTests.cs b/test/Microsoft.OpenApi.Tests/Writers/OpenApiWriterAnyExtensionsTests.cs index 32ec6bab6..372d551d5 100644 --- a/test/Microsoft.OpenApi.Tests/Writers/OpenApiWriterAnyExtensionsTests.cs +++ b/test/Microsoft.OpenApi.Tests/Writers/OpenApiWriterAnyExtensionsTests.cs @@ -165,10 +165,10 @@ from shouldBeTerse in shouldProduceTerseOutputValues public async Task WriteOpenApiDateTimeAsJsonWorksAsync(string inputString, bool produceTerseOutput) { // Arrange - var dateTimeValue = JsonValue.Create(inputString); + var input = DateTimeOffset.Parse(inputString, CultureInfo.InvariantCulture); - var json = await WriteAsJsonAsync(dateTimeValue, produceTerseOutput); - var expectedJson = "\"" + inputString + "\""; + var json = await WriteAsJsonAsync(input, produceTerseOutput); + var expectedJson = "\"" + input.ToString("o") + "\""; // Assert Assert.Equal(expectedJson, json); From e17dc69e8f10aec6a8b09ad7e20831f14478798e Mon Sep 17 00:00:00 2001 From: Maggiekimani1 Date: Mon, 24 Feb 2025 23:06:11 +0300 Subject: [PATCH 5/6] refactor: add support for DateOnly types --- src/Microsoft.OpenApi/Writers/OpenApiWriterAnyExtensions.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Microsoft.OpenApi/Writers/OpenApiWriterAnyExtensions.cs b/src/Microsoft.OpenApi/Writers/OpenApiWriterAnyExtensions.cs index 2601a4393..8dd560160 100644 --- a/src/Microsoft.OpenApi/Writers/OpenApiWriterAnyExtensions.cs +++ b/src/Microsoft.OpenApi/Writers/OpenApiWriterAnyExtensions.cs @@ -117,6 +117,10 @@ private static void WritePrimitive(this IOpenApiWriter writer, JsonValue jsonVal writer.WriteValue(dateTimeValue.ToString("o", CultureInfo.InvariantCulture)); // ISO 8601 format else if (jsonValue.TryGetValue(out DateTimeOffset dateTimeOffsetValue)) writer.WriteValue(dateTimeOffsetValue.ToString("o", CultureInfo.InvariantCulture)); +#if NET6_0_OR_GREATER + else if (jsonValue.TryGetValue(out DateOnly dateOnlyValue)) + writer.WriteValue(dateOnlyValue.ToString("o", CultureInfo.InvariantCulture)); +#endif else if (jsonValue.TryGetValue(out bool boolValue)) writer.WriteValue(boolValue); // write number values From c09218991c9805deeaa6a316069cb9d3d56b00cf Mon Sep 17 00:00:00 2001 From: Maggiekimani1 Date: Wed, 26 Feb 2025 17:56:17 +0300 Subject: [PATCH 6/6] chore: write out timeOnly values --- src/Microsoft.OpenApi/Writers/OpenApiWriterAnyExtensions.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Microsoft.OpenApi/Writers/OpenApiWriterAnyExtensions.cs b/src/Microsoft.OpenApi/Writers/OpenApiWriterAnyExtensions.cs index 8dd560160..f7559f0f7 100644 --- a/src/Microsoft.OpenApi/Writers/OpenApiWriterAnyExtensions.cs +++ b/src/Microsoft.OpenApi/Writers/OpenApiWriterAnyExtensions.cs @@ -120,6 +120,8 @@ private static void WritePrimitive(this IOpenApiWriter writer, JsonValue jsonVal #if NET6_0_OR_GREATER else if (jsonValue.TryGetValue(out DateOnly dateOnlyValue)) writer.WriteValue(dateOnlyValue.ToString("o", CultureInfo.InvariantCulture)); + else if (jsonValue.TryGetValue(out TimeOnly timeOnlyValue)) + writer.WriteValue(timeOnlyValue.ToString("o", CultureInfo.InvariantCulture)); #endif else if (jsonValue.TryGetValue(out bool boolValue)) writer.WriteValue(boolValue);