Skip to content

Commit 4ee1d8b

Browse files
Merge pull request #2160 from microsoft/fix/primitive-parsing-bug
fix: primitive parsing for strings as DateTimes is too greedy
2 parents cf2df41 + c092189 commit 4ee1d8b

File tree

3 files changed

+67
-70
lines changed

3 files changed

+67
-70
lines changed

src/Microsoft.OpenApi/Writers/OpenApiWriterAnyExtensions.cs

Lines changed: 29 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -68,13 +68,13 @@ public static void WriteAny(this IOpenApiWriter writer, JsonNode node)
6868
writer.WriteObject(node as JsonObject);
6969
break;
7070
case JsonValueKind.String: // Primitive
71-
writer.WritePrimitive(node);
71+
writer.WritePrimitive(node.AsValue());
7272
break;
7373
case JsonValueKind.Number: // Primitive
74-
writer.WritePrimitive(node);
74+
writer.WritePrimitive(node.AsValue());
7575
break;
7676
case JsonValueKind.True or JsonValueKind.False: // Primitive
77-
writer.WritePrimitive(node);
77+
writer.WritePrimitive(node.AsValue());
7878
break;
7979
case JsonValueKind.Null: // null
8080
writer.WriteNull();
@@ -109,72 +109,33 @@ private static void WriteObject(this IOpenApiWriter writer, JsonObject entity)
109109
writer.WriteEndObject();
110110
}
111111

112-
private static void WritePrimitive(this IOpenApiWriter writer, JsonNode primitive)
112+
private static void WritePrimitive(this IOpenApiWriter writer, JsonValue jsonValue)
113113
{
114-
Utils.CheckArgumentNull(writer);
115-
116-
var valueKind = primitive.GetValueKind();
117-
118-
if (valueKind == JsonValueKind.String && primitive is JsonValue jsonStrValue)
119-
{
120-
if (jsonStrValue.TryGetValue<DateTimeOffset>(out var dto))
121-
{
122-
writer.WriteValue(dto);
123-
}
124-
else if (jsonStrValue.TryGetValue<DateTime>(out var dt))
125-
{
126-
writer.WriteValue(dt);
127-
}
128-
else if (jsonStrValue.TryGetValue<string>(out var strValue))
129-
{
130-
// check whether string is actual string or date time object
131-
if (DateTimeOffset.TryParse(strValue, CultureInfo.InvariantCulture, DateTimeStyles.RoundtripKind, out var dateTimeOffset))
132-
{
133-
writer.WriteValue(dateTimeOffset);
134-
}
135-
else if (DateTime.TryParse(strValue, CultureInfo.InvariantCulture, DateTimeStyles.RoundtripKind, out var dateTime))
136-
{ // order matters, DTO needs to be checked first!!!
137-
writer.WriteValue(dateTime);
138-
}
139-
else
140-
{
141-
writer.WriteValue(strValue);
142-
}
143-
}
144-
}
145-
146-
else if (valueKind == JsonValueKind.Number && primitive is JsonValue jsonValue)
147-
{
148-
149-
if (jsonValue.TryGetValue<decimal>(out var decimalValue))
150-
{
151-
writer.WriteValue(decimalValue);
152-
}
153-
else if (jsonValue.TryGetValue<double>(out var doubleValue))
154-
{
155-
writer.WriteValue(doubleValue);
156-
}
157-
else if (jsonValue.TryGetValue<float>(out var floatValue))
158-
{
159-
writer.WriteValue(floatValue);
160-
}
161-
else if (jsonValue.TryGetValue<long>(out var longValue))
162-
{
163-
writer.WriteValue(longValue);
164-
}
165-
else if (jsonValue.TryGetValue<int>(out var intValue))
166-
{
167-
writer.WriteValue(intValue);
168-
}
169-
}
170-
else if (valueKind is JsonValueKind.False)
171-
{
172-
writer.WriteValue(false);
173-
}
174-
else if (valueKind is JsonValueKind.True)
175-
{
176-
writer.WriteValue(true);
177-
}
114+
if (jsonValue.TryGetValue(out string stringValue))
115+
writer.WriteValue(stringValue);
116+
else if (jsonValue.TryGetValue(out DateTime dateTimeValue))
117+
writer.WriteValue(dateTimeValue.ToString("o", CultureInfo.InvariantCulture)); // ISO 8601 format
118+
else if (jsonValue.TryGetValue(out DateTimeOffset dateTimeOffsetValue))
119+
writer.WriteValue(dateTimeOffsetValue.ToString("o", CultureInfo.InvariantCulture));
120+
#if NET6_0_OR_GREATER
121+
else if (jsonValue.TryGetValue(out DateOnly dateOnlyValue))
122+
writer.WriteValue(dateOnlyValue.ToString("o", CultureInfo.InvariantCulture));
123+
else if (jsonValue.TryGetValue(out TimeOnly timeOnlyValue))
124+
writer.WriteValue(timeOnlyValue.ToString("o", CultureInfo.InvariantCulture));
125+
#endif
126+
else if (jsonValue.TryGetValue(out bool boolValue))
127+
writer.WriteValue(boolValue);
128+
// write number values
129+
else if (jsonValue.TryGetValue(out decimal decimalValue))
130+
writer.WriteValue(decimalValue);
131+
else if (jsonValue.TryGetValue(out double doubleValue))
132+
writer.WriteValue(doubleValue);
133+
else if (jsonValue.TryGetValue(out float floatValue))
134+
writer.WriteValue(floatValue);
135+
else if (jsonValue.TryGetValue(out long longValue))
136+
writer.WriteValue(longValue);
137+
else if (jsonValue.TryGetValue(out int intValue))
138+
writer.WriteValue(intValue);
178139
}
179140
}
180141
}

test/Microsoft.OpenApi.Readers.Tests/V31Tests/OpenApiSchemaTests.cs

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -537,6 +537,43 @@ public void ParseSchemaWithUnrecognizedKeywordsWorks()
537537
Assert.Equal(2, schema.UnrecognizedKeywords.Count);
538538
}
539539

540+
[Fact]
541+
public void ParseSchemaExampleWithPrimitivesWorks()
542+
{
543+
var expected1 = @"{
544+
""type"": ""string"",
545+
""example"": ""2024-01-02""
546+
}";
547+
548+
var expected2 = @"{
549+
""type"": ""string"",
550+
""example"": ""3.14""
551+
}";
552+
var schema = new OpenApiSchema()
553+
{
554+
Type = JsonSchemaType.String,
555+
Example = JsonValue.Create("2024-01-02")
556+
};
557+
558+
var schema2 = new OpenApiSchema()
559+
{
560+
Type = JsonSchemaType.String,
561+
Example = JsonValue.Create("3.14")
562+
};
563+
564+
var textWriter = new StringWriter();
565+
var writer = new OpenApiJsonWriter(textWriter);
566+
schema.SerializeAsV31(writer);
567+
var actual1 = textWriter.ToString();
568+
Assert.Equal(expected1.MakeLineBreaksEnvironmentNeutral(), actual1.MakeLineBreaksEnvironmentNeutral());
569+
570+
textWriter = new StringWriter();
571+
writer = new OpenApiJsonWriter(textWriter);
572+
schema2.SerializeAsV31(writer);
573+
var actual2 = textWriter.ToString();
574+
Assert.Equal(expected2.MakeLineBreaksEnvironmentNeutral(), actual2.MakeLineBreaksEnvironmentNeutral());
575+
}
576+
540577
[Theory]
541578
[InlineData(JsonSchemaType.Integer | JsonSchemaType.String, new[] { "integer", "string" })]
542579
[InlineData(JsonSchemaType.Integer | JsonSchemaType.Null, new[] { "integer", "null" })]

test/Microsoft.OpenApi.Tests/Writers/OpenApiWriterAnyExtensionsTests.cs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -166,9 +166,8 @@ public async Task WriteOpenApiDateTimeAsJsonWorksAsync(string inputString, bool
166166
{
167167
// Arrange
168168
var input = DateTimeOffset.Parse(inputString, CultureInfo.InvariantCulture);
169-
var dateTimeValue = input;
170169

171-
var json = await WriteAsJsonAsync(dateTimeValue, produceTerseOutput);
170+
var json = await WriteAsJsonAsync(input, produceTerseOutput);
172171
var expectedJson = "\"" + input.ToString("o") + "\"";
173172

174173
// Assert

0 commit comments

Comments
 (0)