Skip to content
Open
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion src/Microsoft.OpenApi.YamlReader/YamlConverter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,9 @@ private static JsonValue ToJsonValue(this YamlScalarNode yaml)
{
return yaml.Style switch
{
ScalarStyle.Plain when decimal.TryParse(yaml.Value, NumberStyles.Float, CultureInfo.InvariantCulture, out var d) => JsonValue.Create(d),
// JsonNode.Parse will create a JsonValue that is suitable for representing any numeric value (it's wrapping JsonElement).
// So, if we call '.TryGetValue<int>' on it and the underlying value can be represented as int, it will succeed.
ScalarStyle.Plain when decimal.TryParse(yaml.Value, NumberStyles.Float, CultureInfo.InvariantCulture, out var d) => (JsonNode.Parse(yaml.Value) as JsonValue) ?? JsonValue.Create(d),
ScalarStyle.Plain when bool.TryParse(yaml.Value, out var b) => JsonValue.Create(b),
Comment on lines +133 to 136
ScalarStyle.Plain when YamlNullRepresentations.Contains(yaml.Value) => (JsonValue)JsonNullSentinel.JsonNull.DeepClone(),
ScalarStyle.Plain => JsonValue.Create(yaml.Value),
Expand Down
41 changes: 40 additions & 1 deletion src/Microsoft.OpenApi/Reader/JsonNodeHelper.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.

using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text.Json;
using System.Text.Json.Nodes;

namespace Microsoft.OpenApi.Reader
Expand Down Expand Up @@ -159,9 +160,47 @@ public static Dictionary<string, HashSet<T>> CreateArrayMap<T>(this JsonNode? no
{
var scalarNode = node is JsonValue value ? value : throw new OpenApiException("Expected scalar value.");

// It's much more efficient to call scalarNode.TryGetValue<string> than to call scalarNode.GetValue<object>() and then convert to string.
// When asking for "object" type, if scalarNode is JsonValueOfElement (internal type in STJ), we will get back
// a boxed JsonElement (paying the cost of unnecessary boxing allocation), and we then call JsonElement.ToString.
// So, we first check if we can get the string value directly, and only if that fails, we fallback to the expensive code.
Comment on lines +163 to +166

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that one reason why we had to do that was to avoid mangling like default, numbers with formats, etc... While I do appreciate the performance improvements, I don't want it to come at the price of a regression.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TryGetValue<string> is never going to throw or do something strange. It will return true if the node really represents a string. For example, if you have any numbers in JSON like "maximum": 10, TryGetValue<string> will return false because the node doesn't represent a string. And then we fallback to the original implementation.

I done it this way explicitly because I saw that some numerics are read raw as strings that way.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is what I'm referring to c17a87e

I think the object convert was introduced to avoid formatting issues for defaults and other properties when using types like dates and such.

if (scalarNode.TryGetValue<string>(out var stringValue))
{
return stringValue;
}

return Convert.ToString(scalarNode.GetValue<object>(), CultureInfo.InvariantCulture);
}

public static bool GetScalarBoolValue(this JsonNode? node)
{
var scalarNode = node is JsonValue value ? value : throw new OpenApiException("Expected scalar value.");
return scalarNode.GetValue<bool>();
}
Comment on lines +175 to +179

public static int GetScalarIntValue(this JsonNode? node)
{
var scalarNode = node is JsonValue value && value.GetValueKind() == JsonValueKind.Number ? value : throw new OpenApiException("Expected numeric scalar value.");
Comment thread
Youssef1313 marked this conversation as resolved.

if (scalarNode.TryGetValue<int>(out var intValue))
{
return intValue;
}

return Convert.ToInt32(scalarNode.GetValue<object>());
}
Comment thread
Youssef1313 marked this conversation as resolved.
Comment on lines +181 to +191

public static uint GetScalarUIntValue(this JsonNode? node)
{
var scalarNode = node is JsonValue value && value.GetValueKind() == JsonValueKind.Number ? value : throw new OpenApiException("Expected numeric scalar value.");
if (scalarNode.TryGetValue<uint>(out var uintValue))
{
return uintValue;
}

return Convert.ToUInt32(scalarNode.GetValue<object>());
}
Comment thread
Youssef1313 marked this conversation as resolved.
Comment on lines +193 to +202

public static string? GetReferencePointer(this JsonObject jsonObject)
{
return jsonObject.TryGetPropertyValue("$ref", out var refNode) ? refNode?.GetScalarValue() : null;
Expand Down
40 changes: 10 additions & 30 deletions src/Microsoft.OpenApi/Reader/V2/OpenApiHeaderDeserializer.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.

using System.Text.Json.Nodes;
Expand Down Expand Up @@ -67,7 +67,7 @@ internal static partial class OpenApiV2Deserializer
},
{
"exclusiveMaximum",
(o, n, _, _) => GetOrCreateSchema(o).IsExclusiveMaximum = bool.Parse(n.GetScalarValue()!)
(o, n, _, _) => GetOrCreateSchema(o).IsExclusiveMaximum = n.GetScalarBoolValue()
},
{
"minimum",
Expand All @@ -76,34 +76,26 @@ internal static partial class OpenApiV2Deserializer
var min = n.GetScalarValue();
if (!string.IsNullOrEmpty(min))
{
GetOrCreateSchema(o).Minimum = min;
}
GetOrCreateSchema(o).Minimum = min;
}
}
},
{
"exclusiveMinimum",
(o, n, _, _) => GetOrCreateSchema(o).IsExclusiveMinimum = bool.Parse(n.GetScalarValue()!)
(o, n, _, _) => GetOrCreateSchema(o).IsExclusiveMinimum = n.GetScalarBoolValue()
},
{
"maxLength",
(o, n, _, _) =>
{
var maxLength = n.GetScalarValue();
if (maxLength != null)
{
GetOrCreateSchema(o).MaxLength = int.Parse(maxLength, CultureInfo.InvariantCulture);
}
GetOrCreateSchema(o).MaxLength = n.GetScalarIntValue();
}
},
{
"minLength",
(o, n, _, _) =>
{
var minLength = n.GetScalarValue();
if (minLength != null)
{
GetOrCreateSchema(o).MinLength = int.Parse(minLength, CultureInfo.InvariantCulture);
}
GetOrCreateSchema(o).MinLength = n.GetScalarIntValue();
}
},
{
Expand All @@ -114,33 +106,21 @@ internal static partial class OpenApiV2Deserializer
"maxItems",
(o, n, _, _) =>
{
var maxItems = n.GetScalarValue();
if (maxItems != null)
{
GetOrCreateSchema(o).MaxItems = int.Parse(maxItems, CultureInfo.InvariantCulture);
}
GetOrCreateSchema(o).MaxItems = n.GetScalarIntValue();
}
},
{
"minItems",
(o, n, _, _) =>
{
var minItems = n.GetScalarValue();
if (minItems != null)
{
GetOrCreateSchema(o).MinItems = int.Parse(minItems, CultureInfo.InvariantCulture);
}
GetOrCreateSchema(o).MinItems = n.GetScalarIntValue();
}
},
{
"uniqueItems",
(o, n, _, _) =>
{
var uniqueItems = n.GetScalarValue();
if (uniqueItems != null)
{
GetOrCreateSchema(o).UniqueItems = bool.Parse(uniqueItems);
}
GetOrCreateSchema(o).UniqueItems = n.GetScalarBoolValue();
}
},
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.

using System.Collections.Generic;
Expand Down Expand Up @@ -82,11 +82,7 @@ internal static partial class OpenApiV2Deserializer
"deprecated",
(o, n, _, _) =>
{
var deprecated = n.GetScalarValue();
if (deprecated != null)
{
o.Deprecated = bool.Parse(deprecated);
}
o.Deprecated = n.GetScalarBoolValue();
}
},
{
Expand Down
38 changes: 7 additions & 31 deletions src/Microsoft.OpenApi/Reader/V2/OpenApiParameterDeserializer.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.

using System.Text.Json.Nodes;
Expand Down Expand Up @@ -34,33 +34,21 @@ internal static partial class OpenApiV2Deserializer
"required",
(o, n, _, _) =>
{
var required = n.GetScalarValue();
if (required != null)
{
o.Required = bool.Parse(required);
}
o.Required = n.GetScalarBoolValue();
}
},
{
"deprecated",
(o, n, _, _) =>
{
var deprecated = n.GetScalarValue();
if (deprecated != null)
{
o.Deprecated = bool.Parse(deprecated);
}
o.Deprecated = n.GetScalarBoolValue();
}
},
{
"allowEmptyValue",
(o, n, _, _) =>
{
var allowEmptyValue = n.GetScalarValue();
if (allowEmptyValue != null)
{
o.AllowEmptyValue = bool.Parse(allowEmptyValue);
}
o.AllowEmptyValue = n.GetScalarBoolValue();
}
},
{
Expand Down Expand Up @@ -124,33 +112,21 @@ internal static partial class OpenApiV2Deserializer
"maxLength",
(o, n, _, _) =>
{
var maxLength = n.GetScalarValue();
if (maxLength != null)
{
GetOrCreateSchema(o).MaxLength = int.Parse(maxLength, CultureInfo.InvariantCulture);
}
GetOrCreateSchema(o).MaxLength = n.GetScalarIntValue();
}
},
{
"minLength",
(o, n, _, _) =>
{
var minLength = n.GetScalarValue();
if (minLength != null)
{
GetOrCreateSchema(o).MinLength = int.Parse(minLength, CultureInfo.InvariantCulture);
}
GetOrCreateSchema(o).MinLength = n.GetScalarIntValue();
}
},
{
"readOnly",
(o, n, _, _) =>
{
var readOnly = n.GetScalarValue();
if (readOnly != null)
{
GetOrCreateSchema(o).ReadOnly = bool.Parse(readOnly);
}
GetOrCreateSchema(o).ReadOnly = n.GetScalarBoolValue();
}
},
{
Expand Down
56 changes: 12 additions & 44 deletions src/Microsoft.OpenApi/Reader/V2/OpenApiSchemaDeserializer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ internal static partial class OpenApiV2Deserializer
},
{
"exclusiveMaximum",
(o, n, _, _) => o.IsExclusiveMaximum = bool.Parse(n.GetScalarValue()!)
(o, n, _, _) => o.IsExclusiveMaximum = n.GetScalarBoolValue()
},
{
"minimum",
Expand All @@ -63,7 +63,7 @@ internal static partial class OpenApiV2Deserializer
},
{
"exclusiveMinimum",
(o, n, _, _) => o.IsExclusiveMinimum = bool.Parse(n.GetScalarValue()!)
(o, n, _, _) => o.IsExclusiveMinimum = n.GetScalarBoolValue()
},
{
"maxLength",
Expand All @@ -72,19 +72,15 @@ internal static partial class OpenApiV2Deserializer
var maxLength = n.GetScalarValue();
if (maxLength != null)
{
o.MaxLength = int.Parse(maxLength, CultureInfo.InvariantCulture);
o.MaxLength = n.GetScalarIntValue();
}
}
},
{
"minLength",
(o, n, _, _) =>
{
var minLength = n.GetScalarValue();
if (minLength != null)
{
o.MinLength = int.Parse(minLength, CultureInfo.InvariantCulture);
}
o.MinLength = n.GetScalarIntValue();
}
},
{
Expand All @@ -95,55 +91,35 @@ internal static partial class OpenApiV2Deserializer
"maxItems",
(o, n, _, _) =>
{
var maxItems = n.GetScalarValue();
if (maxItems != null)
{
o.MaxItems = int.Parse(maxItems, CultureInfo.InvariantCulture);
}
o.MaxItems = n.GetScalarIntValue();
}
},
{
"minItems",
(o, n, _, _) =>
{
var minItems = n.GetScalarValue();
if (minItems != null)
{
o.MinItems = int.Parse(minItems, CultureInfo.InvariantCulture);
}
o.MinItems = n.GetScalarIntValue();
}
},
{
"uniqueItems",
(o, n, _, _) =>
{
var uniqueItems = n.GetScalarValue();
if (uniqueItems != null)
{
o.UniqueItems = bool.Parse(uniqueItems);
}
o.UniqueItems = n.GetScalarBoolValue();
}
},
{
"maxProperties",
(o, n, _, _) =>
{
var maxProps = n.GetScalarValue();
if (maxProps != null)
{
o.MaxProperties = int.Parse(maxProps, CultureInfo.InvariantCulture);
}
o.MaxProperties = n.GetScalarIntValue();
}
},
{
"minProperties",
(o, n, _, _) =>
{
var minProps = n.GetScalarValue();
if (minProps != null)
{
o.MinProperties = int.Parse(minProps, CultureInfo.InvariantCulture);
}
o.MinProperties = n.GetScalarIntValue();
}
},
{
Expand Down Expand Up @@ -188,11 +164,7 @@ internal static partial class OpenApiV2Deserializer
{
if (n is JsonValue)
{
var value = n.GetScalarValue();
if (value is not null)
{
o.AdditionalPropertiesAllowed = bool.Parse(value);
}
o.AdditionalPropertiesAllowed = n.GetScalarBoolValue();
}
else
{
Expand All @@ -216,7 +188,7 @@ internal static partial class OpenApiV2Deserializer
OpenApiConstants.NullableExtension,
(o, n, _, _) =>
{
if (bool.TryParse(n.GetScalarValue(), out var parsed) && parsed)
if (n.GetScalarBoolValue())
{
if (o.Type is not null && o.Type != 0)
{
Expand Down Expand Up @@ -248,11 +220,7 @@ internal static partial class OpenApiV2Deserializer
"readOnly",
(o, n, _, _) =>
{
var readOnly = n.GetScalarValue();
if (readOnly is not null)
{
o.ReadOnly = bool.Parse(readOnly);
}
o.ReadOnly = n.GetScalarBoolValue();
}
},
{
Expand Down
Loading
Loading