From 234504c6a1a53be8630b1f2eda8640f04a92327d Mon Sep 17 00:00:00 2001 From: Vincent Biret Date: Fri, 24 Oct 2025 11:48:11 -0400 Subject: [PATCH 1/4] fix: an issue where numeric property names would be missing quotes in yaml conversion Signed-off-by: Vincent Biret --- .../YamlConverter.cs | 5 +- .../YamlConverterTests.cs | 55 ++++++++++++++++++- 2 files changed, 56 insertions(+), 4 deletions(-) diff --git a/src/Microsoft.OpenApi.YamlReader/YamlConverter.cs b/src/Microsoft.OpenApi.YamlReader/YamlConverter.cs index 260e4d971..1c794a11d 100644 --- a/src/Microsoft.OpenApi.YamlReader/YamlConverter.cs +++ b/src/Microsoft.OpenApi.YamlReader/YamlConverter.cs @@ -87,7 +87,10 @@ public static JsonObject ToJsonObject(this YamlMappingNode yaml) private static YamlMappingNode ToYamlMapping(this JsonObject obj) { - return new YamlMappingNode(obj.ToDictionary(x => (YamlNode)new YamlScalarNode(x.Key), x => x.Value!.ToYamlNode())); + return new YamlMappingNode(obj.ToDictionary(x => (YamlNode)new YamlScalarNode(x.Key) + { + Style = double.TryParse(x.Key, out _) ? ScalarStyle.DoubleQuoted : ScalarStyle.Plain + }, x => x.Value!.ToYamlNode())); } /// diff --git a/test/Microsoft.OpenApi.Readers.Tests/YamlConverterTests.cs b/test/Microsoft.OpenApi.Readers.Tests/YamlConverterTests.cs index a40febbbf..318484bef 100644 --- a/test/Microsoft.OpenApi.Readers.Tests/YamlConverterTests.cs +++ b/test/Microsoft.OpenApi.Readers.Tests/YamlConverterTests.cs @@ -1,4 +1,5 @@ -using Microsoft.OpenApi.YamlReader; +using Microsoft.OpenApi.Tests; +using Microsoft.OpenApi.YamlReader; using SharpYaml; using SharpYaml.Serialization; using System.IO; @@ -225,12 +226,60 @@ public void ToYamlNode_StringWithLineBreaks_PreservesLineBreaks() Assert.Contains("\n", roundTripDescription); } + [Fact] + public void NumericPropertyNamesShouldRemainStringsFromJson() + { + // Given + var yamlInput = + """ + "123": value1 + "456": value2 + """; + + // Given + var jsonNode = Assert.IsType(JsonNode.Parse(@"{ + ""123"": ""value1"", + ""456"": ""value2"" + }")); + + // When + var convertedBack = jsonNode.ToYamlNode(); + var convertedBackOutput = ConvertYamlNodeToString(convertedBack); + + // Then + Assert.Equal(yamlInput.MakeLineBreaksEnvironmentNeutral(), convertedBackOutput.MakeLineBreaksEnvironmentNeutral()); + } + + [Fact] + public void NumericPropertyNamesShouldRemainStringsFromYaml() + { + // Given + var yamlInput = + """ + "123": value1 + "456": value2 + """; + + var yamlDocument = new YamlStream(); + using var sr = new StringReader(yamlInput); + yamlDocument.Load(sr); + var yamlRoot = yamlDocument.Documents[0].RootNode; + // When + + var jsonNode = yamlRoot.ToJsonNode(); + + var convertedBack = jsonNode.ToYamlNode(); + var convertedBackOutput = ConvertYamlNodeToString(convertedBack); + // Then + Assert.Equal(yamlInput.MakeLineBreaksEnvironmentNeutral(), convertedBackOutput.MakeLineBreaksEnvironmentNeutral()); + } + private static string ConvertYamlNodeToString(YamlNode yamlNode) { using var ms = new MemoryStream(); - var yamlStream = new YamlStream(new YamlDocument(yamlNode)); + var document = new YamlStream(new YamlDocument(yamlNode)); var writer = new StreamWriter(ms); - yamlStream.Save(writer); + document.Save(writer, isLastDocumentEndImplicit: true); writer.Flush(); ms.Seek(0, SeekOrigin.Begin); var reader = new StreamReader(ms); From 39a9f4112a123b9207504d4a840a9be553703555 Mon Sep 17 00:00:00 2001 From: Vincent Biret Date: Fri, 24 Oct 2025 13:21:28 -0400 Subject: [PATCH 2/4] fix: quote property names in yaml that match boolean values Signed-off-by: Vincent Biret --- .../YamlConverter.cs | 11 +++++---- .../YamlConverterTests.cs | 24 +++++++++++++++++++ 2 files changed, 31 insertions(+), 4 deletions(-) diff --git a/src/Microsoft.OpenApi.YamlReader/YamlConverter.cs b/src/Microsoft.OpenApi.YamlReader/YamlConverter.cs index 1c794a11d..d76cf8ec8 100644 --- a/src/Microsoft.OpenApi.YamlReader/YamlConverter.cs +++ b/src/Microsoft.OpenApi.YamlReader/YamlConverter.cs @@ -89,7 +89,7 @@ private static YamlMappingNode ToYamlMapping(this JsonObject obj) { return new YamlMappingNode(obj.ToDictionary(x => (YamlNode)new YamlScalarNode(x.Key) { - Style = double.TryParse(x.Key, out _) ? ScalarStyle.DoubleQuoted : ScalarStyle.Plain + Style = NeedsQuoting(x.Key) ? ScalarStyle.DoubleQuoted : ScalarStyle.Plain }, x => x.Value!.ToYamlNode())); } @@ -135,6 +135,11 @@ ScalarStyle.Plain when YamlNullRepresentations.Contains(yaml.Value) => (JsonValu }; } + private static bool NeedsQuoting(string value) => + decimal.TryParse(value, NumberStyles.Float, CultureInfo.InvariantCulture, out _) || + bool.TryParse(value, out _) || + YamlNullRepresentations.Contains(value); + private static YamlScalarNode ToYamlScalar(this JsonValue val) { // Try to get the underlying value based on its actual type @@ -145,9 +150,7 @@ private static YamlScalarNode ToYamlScalar(this JsonValue val) // For string values, we need to determine if they should be quoted in YAML // Strings that look like numbers, booleans, or null need to be quoted // to preserve their string type when round-tripping - var needsQuoting = decimal.TryParse(stringValue, NumberStyles.Float, CultureInfo.InvariantCulture, out _) || - bool.TryParse(stringValue, out _) || - YamlNullRepresentations.Contains(stringValue); + var needsQuoting = NeedsQuoting(stringValue); return new YamlScalarNode(stringValue) { diff --git a/test/Microsoft.OpenApi.Readers.Tests/YamlConverterTests.cs b/test/Microsoft.OpenApi.Readers.Tests/YamlConverterTests.cs index 318484bef..4348f95db 100644 --- a/test/Microsoft.OpenApi.Readers.Tests/YamlConverterTests.cs +++ b/test/Microsoft.OpenApi.Readers.Tests/YamlConverterTests.cs @@ -274,6 +274,30 @@ public void NumericPropertyNamesShouldRemainStringsFromYaml() Assert.Equal(yamlInput.MakeLineBreaksEnvironmentNeutral(), convertedBackOutput.MakeLineBreaksEnvironmentNeutral()); } + [Fact] + public void BooleanPropertyNamesShouldRemainStringsFromYaml() + { + // Given + var yamlInput = + """ + "true": value1 + "false": value2 + """; + + var yamlDocument = new YamlStream(); + using var sr = new StringReader(yamlInput); + yamlDocument.Load(sr); + var yamlRoot = yamlDocument.Documents[0].RootNode; + // When + + var jsonNode = yamlRoot.ToJsonNode(); + + var convertedBack = jsonNode.ToYamlNode(); + var convertedBackOutput = ConvertYamlNodeToString(convertedBack); + // Then + Assert.Equal(yamlInput.MakeLineBreaksEnvironmentNeutral(), convertedBackOutput.MakeLineBreaksEnvironmentNeutral()); + } + private static string ConvertYamlNodeToString(YamlNode yamlNode) { using var ms = new MemoryStream(); From 1ad4edef1ae4803572e5aafad25f8f3735493a4f Mon Sep 17 00:00:00 2001 From: Vincent Biret Date: Fri, 24 Oct 2025 13:24:14 -0400 Subject: [PATCH 3/4] chore: refactoring Signed-off-by: Vincent Biret --- .../YamlConverterTests.cs | 29 +++++++------------ 1 file changed, 11 insertions(+), 18 deletions(-) diff --git a/test/Microsoft.OpenApi.Readers.Tests/YamlConverterTests.cs b/test/Microsoft.OpenApi.Readers.Tests/YamlConverterTests.cs index 4348f95db..fe69012ae 100644 --- a/test/Microsoft.OpenApi.Readers.Tests/YamlConverterTests.cs +++ b/test/Microsoft.OpenApi.Readers.Tests/YamlConverterTests.cs @@ -209,10 +209,7 @@ public void ToYamlNode_StringWithLineBreaks_PreservesLineBreaks() var yamlOutput = ConvertYamlNodeToString(yamlNode); // Convert back to JSON to verify round-tripping - var yamlStream = new YamlStream(); - using var sr = new StringReader(yamlOutput); - yamlStream.Load(sr); - var jsonBack = yamlStream.Documents[0].ToJsonNode(); + var jsonBack = ConvertYamlStringToJsonNode(yamlOutput); // Assert - line breaks should be preserved during round-trip var originalMultiline = json["multiline"]?.GetValue(); @@ -260,13 +257,7 @@ public void NumericPropertyNamesShouldRemainStringsFromYaml() "456": value2 """; - var yamlDocument = new YamlStream(); - using var sr = new StringReader(yamlInput); - yamlDocument.Load(sr); - var yamlRoot = yamlDocument.Documents[0].RootNode; - // When - - var jsonNode = yamlRoot.ToJsonNode(); + var jsonNode = ConvertYamlStringToJsonNode(yamlInput); var convertedBack = jsonNode.ToYamlNode(); var convertedBackOutput = ConvertYamlNodeToString(convertedBack); @@ -284,19 +275,21 @@ public void BooleanPropertyNamesShouldRemainStringsFromYaml() "false": value2 """; - var yamlDocument = new YamlStream(); - using var sr = new StringReader(yamlInput); - yamlDocument.Load(sr); - var yamlRoot = yamlDocument.Documents[0].RootNode; - // When - - var jsonNode = yamlRoot.ToJsonNode(); + var jsonNode = ConvertYamlStringToJsonNode(yamlInput); var convertedBack = jsonNode.ToYamlNode(); var convertedBackOutput = ConvertYamlNodeToString(convertedBack); // Then Assert.Equal(yamlInput.MakeLineBreaksEnvironmentNeutral(), convertedBackOutput.MakeLineBreaksEnvironmentNeutral()); } + private static JsonNode ConvertYamlStringToJsonNode(string yamlInput) + { + var yamlDocument = new YamlStream(); + using var sr = new StringReader(yamlInput); + yamlDocument.Load(sr); + var yamlRoot = yamlDocument.Documents[0].RootNode; + return yamlRoot.ToJsonNode(); + } private static string ConvertYamlNodeToString(YamlNode yamlNode) { From 974ab34ff6ed9afa4f243d2b89da710773deff92 Mon Sep 17 00:00:00 2001 From: Vincent Biret Date: Fri, 24 Oct 2025 13:25:15 -0400 Subject: [PATCH 4/4] chore: reverts unnecessary change Signed-off-by: Vincent Biret --- test/Microsoft.OpenApi.Readers.Tests/YamlConverterTests.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/Microsoft.OpenApi.Readers.Tests/YamlConverterTests.cs b/test/Microsoft.OpenApi.Readers.Tests/YamlConverterTests.cs index fe69012ae..9fa456bab 100644 --- a/test/Microsoft.OpenApi.Readers.Tests/YamlConverterTests.cs +++ b/test/Microsoft.OpenApi.Readers.Tests/YamlConverterTests.cs @@ -294,9 +294,9 @@ private static JsonNode ConvertYamlStringToJsonNode(string yamlInput) private static string ConvertYamlNodeToString(YamlNode yamlNode) { using var ms = new MemoryStream(); - var document = new YamlStream(new YamlDocument(yamlNode)); + var yamlStream = new YamlStream(new YamlDocument(yamlNode)); var writer = new StreamWriter(ms); - document.Save(writer, isLastDocumentEndImplicit: true); + yamlStream.Save(writer, isLastDocumentEndImplicit: true); writer.Flush(); ms.Seek(0, SeekOrigin.Begin); var reader = new StreamReader(ms);