diff --git a/src/Microsoft.OpenApi/Reader/V31/OpenApiSchemaDeserializer.cs b/src/Microsoft.OpenApi/Reader/V31/OpenApiSchemaDeserializer.cs index 9d62a681f..690180fb6 100644 --- a/src/Microsoft.OpenApi/Reader/V31/OpenApiSchemaDeserializer.cs +++ b/src/Microsoft.OpenApi/Reader/V31/OpenApiSchemaDeserializer.cs @@ -7,407 +7,405 @@ using System.Linq; using System.Text.Json.Nodes; -namespace Microsoft.OpenApi.Reader.V31 +namespace Microsoft.OpenApi.Reader.V31; +internal static partial class OpenApiV31Deserializer { - internal static partial class OpenApiV31Deserializer + private static readonly FixedFieldMap _openApiSchemaFixedFields = new() { - private static readonly FixedFieldMap _openApiSchemaFixedFields = new() { + "title", + (o, n, _) => o.Title = n.GetScalarValue() + }, + { + "$schema", + (o, n, _) => { if (n.GetScalarValue() is string {} sSchema && Uri.TryCreate(sSchema, UriKind.Absolute, out var schema)) {o.Schema = schema;}} + }, + { + "$id", + (o, n, _) => o.Id = n.GetScalarValue() + }, + { + "$comment", + (o, n, _) => o.Comment = n.GetScalarValue() + }, + { + "$vocabulary", + (o, n, _) => o.Vocabulary = n.CreateSimpleMap(LoadBool).ToDictionary(kvp => kvp.Key, kvp => kvp.Value ?? false) + }, + { + "$dynamicRef", + (o, n, _) => o.DynamicRef = n.GetScalarValue() + }, + { + "$dynamicAnchor", + (o, n, _) => o.DynamicAnchor = n.GetScalarValue() + }, + { + "$defs", + (o, n, t) => o.Definitions = n.CreateMap(LoadSchema, t) + }, { - "title", - (o, n, _) => o.Title = n.GetScalarValue() - }, - { - "$schema", - (o, n, _) => { if (n.GetScalarValue() is string {} sSchema && Uri.TryCreate(sSchema, UriKind.Absolute, out var schema)) {o.Schema = schema;}} - }, - { - "$id", - (o, n, _) => o.Id = n.GetScalarValue() - }, - { - "$comment", - (o, n, _) => o.Comment = n.GetScalarValue() - }, - { - "$vocabulary", - (o, n, _) => o.Vocabulary = n.CreateSimpleMap(LoadBool).ToDictionary(kvp => kvp.Key, kvp => kvp.Value ?? false) - }, - { - "$dynamicRef", - (o, n, _) => o.DynamicRef = n.GetScalarValue() - }, - { - "$dynamicAnchor", - (o, n, _) => o.DynamicAnchor = n.GetScalarValue() - }, + "multipleOf", + (o, n, _) => { - "$defs", - (o, n, t) => o.Definitions = n.CreateMap(LoadSchema, t) - }, - { - "multipleOf", - (o, n, _) => + var multipleOf = n.GetScalarValue(); + if (multipleOf != null) { - var multipleOf = n.GetScalarValue(); - if (multipleOf != null) - { - o.MultipleOf = decimal.Parse(multipleOf, NumberStyles.Float, CultureInfo.InvariantCulture); - } + o.MultipleOf = decimal.Parse(multipleOf, NumberStyles.Float, CultureInfo.InvariantCulture); } - }, + } + }, + { + "maximum", + (o, n,_) => { - "maximum", - (o, n,_) => + var max = n.GetScalarValue(); + if (!string.IsNullOrEmpty(max)) { - var max = n.GetScalarValue(); - if (!string.IsNullOrEmpty(max)) - { - o.Maximum = max; - } + o.Maximum = max; } - }, - { - "exclusiveMaximum", - (o, n, _) => o.ExclusiveMaximum = n.GetScalarValue() - }, + } + }, + { + "exclusiveMaximum", + (o, n, _) => o.ExclusiveMaximum = n.GetScalarValue() + }, + { + "minimum", + (o, n, _) => { - "minimum", - (o, n, _) => + var min = n.GetScalarValue(); + if (!string.IsNullOrEmpty(min)) { - var min = n.GetScalarValue(); - if (!string.IsNullOrEmpty(min)) - { - o.Minimum = min; - } + o.Minimum = min; } - }, - { - "exclusiveMinimum", - (o, n, _) => o.ExclusiveMinimum = n.GetScalarValue() - }, + } + }, + { + "exclusiveMinimum", + (o, n, _) => o.ExclusiveMinimum = n.GetScalarValue() + }, + { + "maxLength", + (o, n, _) => { - "maxLength", - (o, n, _) => + var maxLength = n.GetScalarValue(); + if (maxLength != null) { - var maxLength = n.GetScalarValue(); - if (maxLength != null) - { - o.MaxLength = int.Parse(maxLength, CultureInfo.InvariantCulture); - } + o.MaxLength = int.Parse(maxLength, CultureInfo.InvariantCulture); } - }, + } + }, + { + "minLength", + (o, n, _) => { - "minLength", - (o, n, _) => + var minLength = n.GetScalarValue(); + if (minLength != null) { - var minLength = n.GetScalarValue(); - if (minLength != null) - { - o.MinLength = int.Parse(minLength, CultureInfo.InvariantCulture); - } + o.MinLength = int.Parse(minLength, CultureInfo.InvariantCulture); } - }, - { - "pattern", - (o, n, _) => o.Pattern = n.GetScalarValue() - }, + } + }, + { + "pattern", + (o, n, _) => o.Pattern = n.GetScalarValue() + }, + { + "maxItems", + (o, n, _) => { - "maxItems", - (o, n, _) => + var maxItems = n.GetScalarValue(); + if (maxItems != null) { - var maxItems = n.GetScalarValue(); - if (maxItems != null) - { - o.MaxItems = int.Parse(maxItems, CultureInfo.InvariantCulture); - } + o.MaxItems = int.Parse(maxItems, CultureInfo.InvariantCulture); } - }, + } + }, + { + "minItems", + (o, n, _) => { - "minItems", - (o, n, _) => + var minItems = n.GetScalarValue(); + if (minItems != null) { - var minItems = n.GetScalarValue(); - if (minItems != null) - { - o.MinItems = int.Parse(minItems, CultureInfo.InvariantCulture); - } + o.MinItems = int.Parse(minItems, CultureInfo.InvariantCulture); } - }, + } + }, + { + "uniqueItems", + (o, n, _) => { - "uniqueItems", - (o, n, _) => + var uniqueItems = n.GetScalarValue(); + if (uniqueItems != null) { - var uniqueItems = n.GetScalarValue(); - if (uniqueItems != null) - { - o.UniqueItems = bool.Parse(uniqueItems); - } + o.UniqueItems = bool.Parse(uniqueItems); } - }, + } + }, + { + "unevaluatedProperties", + (o, n, _) => { - "unevaluatedProperties", - (o, n, _) => + var unevaluatedProps = n.GetScalarValue(); + if (unevaluatedProps != null) { - var unevaluatedProps = n.GetScalarValue(); - if (unevaluatedProps != null) - { - o.UnevaluatedProperties = bool.Parse(unevaluatedProps); - } + o.UnevaluatedProperties = bool.Parse(unevaluatedProps); } - }, + } + }, + { + "maxProperties", + (o, n, _) => { - "maxProperties", - (o, n, _) => + var maxProps = n.GetScalarValue(); + if (maxProps != null) { - var maxProps = n.GetScalarValue(); - if (maxProps != null) - { - o.MaxProperties = int.Parse(maxProps, CultureInfo.InvariantCulture); - } + o.MaxProperties = int.Parse(maxProps, CultureInfo.InvariantCulture); } - }, + } + }, + { + "minProperties", + (o, n, _) => { - "minProperties", - (o, n, _) => + var minProps = n.GetScalarValue(); + if (minProps != null) { - var minProps = n.GetScalarValue(); - if (minProps != null) - { - o.MinProperties = int.Parse(minProps, CultureInfo.InvariantCulture); - } + o.MinProperties = int.Parse(minProps, CultureInfo.InvariantCulture); } - }, - { - "required", - (o, n, doc) => o.Required = new HashSet(n.CreateSimpleList((n2, p) => n2.GetScalarValue(), doc).Where(s => s != null)) - }, - { - "enum", - (o, n, _) => o.Enum = n.CreateListOfAny() - }, + } + }, + { + "required", + (o, n, doc) => o.Required = new HashSet(n.CreateSimpleList((n2, p) => n2.GetScalarValue(), doc).Where(s => s != null)) + }, + { + "enum", + (o, n, _) => o.Enum = n.CreateListOfAny() + }, + { + "type", + (o, n, doc) => { - "type", - (o, n, doc) => + if (n is ValueNode) { - if (n is ValueNode) - { - o.Type = n.GetScalarValue()?.ToJsonSchemaType(); - } - else - { - var list = n.CreateSimpleList((n2, p) => n2.GetScalarValue(), doc); - JsonSchemaType combinedType = 0; - foreach(var type in list) - { - if (type is not null) - { - var schemaType = type.ToJsonSchemaType(); - combinedType |= schemaType; - } - } - o.Type = combinedType; - } + o.Type = n.GetScalarValue()?.ToJsonSchemaType(); } - }, - { - "const", - (o, n, _) => o.Const = n.GetScalarValue() - }, - { - "allOf", - (o, n, t) => o.AllOf = n.CreateList(LoadSchema, t) - }, - { - "oneOf", - (o, n, t) => o.OneOf = n.CreateList(LoadSchema, t) - }, - { - "anyOf", - (o, n, t) => o.AnyOf = n.CreateList(LoadSchema, t) - }, - { - "not", - (o, n, doc) => o.Not = LoadSchema(n, doc) - }, - { - "items", - (o, n, doc) => o.Items = LoadSchema(n, doc) - }, - { - "properties", - (o, n, t) => o.Properties = n.CreateMap(LoadSchema, t) - }, - { - "patternProperties", - (o, n, t) => o.PatternProperties = n.CreateMap(LoadSchema, t) - }, - { - "additionalProperties", (o, n, doc) => + else { - if (n is ValueNode) + var list = n.CreateSimpleList((n2, p) => n2.GetScalarValue(), doc); + JsonSchemaType combinedType = 0; + foreach(var type in list) { - var value = n.GetScalarValue(); - if (value is not null) + if (type is not null) { - o.AdditionalPropertiesAllowed = bool.Parse(value); - } - } - else - { - o.AdditionalProperties = LoadSchema(n, doc); + var schemaType = type.ToJsonSchemaType(); + combinedType |= schemaType; + } } + o.Type = combinedType; } - }, - { - "description", - (o, n, _) => o.Description = n.GetScalarValue() - }, - { - "format", - (o, n, _) => o.Format = n.GetScalarValue() - }, - { - "default", - (o, n, _) => o.Default = n.CreateAny() - }, + } + }, + { + "const", + (o, n, _) => o.Const = n.GetScalarValue() + }, + { + "allOf", + (o, n, t) => o.AllOf = n.CreateList(LoadSchema, t) + }, + { + "oneOf", + (o, n, t) => o.OneOf = n.CreateList(LoadSchema, t) + }, + { + "anyOf", + (o, n, t) => o.AnyOf = n.CreateList(LoadSchema, t) + }, + { + "not", + (o, n, doc) => o.Not = LoadSchema(n, doc) + }, + { + "items", + (o, n, doc) => o.Items = LoadSchema(n, doc) + }, + { + "properties", + (o, n, t) => o.Properties = n.CreateMap(LoadSchema, t) + }, + { + "patternProperties", + (o, n, t) => o.PatternProperties = n.CreateMap(LoadSchema, t) + }, + { + "additionalProperties", (o, n, doc) => { - "nullable", - (o, n, _) => + if (n is ValueNode) { var value = n.GetScalarValue(); if (value is not null) { - var nullable = bool.Parse(value); - if (nullable) // if nullable, convert type into an array of type(s) and null - { - if (o.Type.HasValue) - o.Type |= JsonSchemaType.Null; - else - o.Type = JsonSchemaType.Null; - } + o.AdditionalPropertiesAllowed = bool.Parse(value); } } - }, - { - "discriminator", - (o, n, doc) => o.Discriminator = LoadDiscriminator(n, doc) - }, - { - "readOnly", - (o, n, _) => + else { - var readOnly = n.GetScalarValue(); - if (readOnly != null) - { - o.ReadOnly = bool.Parse(readOnly); - } + o.AdditionalProperties = LoadSchema(n, doc); } - }, + } + }, + { + "description", + (o, n, _) => o.Description = n.GetScalarValue() + }, + { + "format", + (o, n, _) => o.Format = n.GetScalarValue() + }, + { + "default", + (o, n, _) => o.Default = n.CreateAny() + }, + { + "nullable", + (o, n, _) => { - "writeOnly", - (o, n, _) => + var value = n.GetScalarValue(); + if (value is not null) { - var writeOnly = n.GetScalarValue(); - if (writeOnly != null) + var nullable = bool.Parse(value); + if (nullable) // if nullable, convert type into an array of type(s) and null { - o.WriteOnly = bool.Parse(writeOnly); + if (o.Type.HasValue) + o.Type |= JsonSchemaType.Null; + else + o.Type = JsonSchemaType.Null; } } - }, - { - "xml", - (o, n, doc) => o.Xml = LoadXml(n, doc) - }, - { - "externalDocs", - (o, n, doc) => o.ExternalDocs = LoadExternalDocs(n, doc) - }, - { - "example", - (o, n, _) => o.Example = n.CreateAny() - }, + } + }, + { + "discriminator", + (o, n, doc) => o.Discriminator = LoadDiscriminator(n, doc) + }, + { + "readOnly", + (o, n, _) => { - "examples", - (o, n, _) => o.Examples = n.CreateListOfAny() - }, + var readOnly = n.GetScalarValue(); + if (readOnly != null) + { + o.ReadOnly = bool.Parse(readOnly); + } + } + }, + { + "writeOnly", + (o, n, _) => { - "deprecated", - (o, n, t) => + var writeOnly = n.GetScalarValue(); + if (writeOnly != null) { - var deprecated = n.GetScalarValue(); - if (deprecated != null) - { - o.Deprecated = bool.Parse(deprecated); - } + o.WriteOnly = bool.Parse(writeOnly); } - }, + } + }, + { + "xml", + (o, n, doc) => o.Xml = LoadXml(n, doc) + }, + { + "externalDocs", + (o, n, doc) => o.ExternalDocs = LoadExternalDocs(n, doc) + }, + { + "example", + (o, n, _) => o.Example = n.CreateAny() + }, + { + "examples", + (o, n, _) => o.Examples = n.CreateListOfAny() + }, + { + "deprecated", + (o, n, t) => { - "dependentRequired", - (o, n, doc) => + var deprecated = n.GetScalarValue(); + if (deprecated != null) { - o.DependentRequired = n.CreateArrayMap((n2, p) => n2.GetScalarValue(), doc); + o.Deprecated = bool.Parse(deprecated); } - }, - }; - - private static readonly PatternFieldMap _openApiSchemaPatternFields = new() + } + }, { - {s => s.StartsWith(OpenApiConstants.ExtensionFieldNamePrefix, StringComparison.OrdinalIgnoreCase), (o, p, n, _) => o.AddExtension(p, LoadExtension(p,n))} - }; + "dependentRequired", + (o, n, doc) => + { + o.DependentRequired = n.CreateArrayMap((n2, p) => n2.GetScalarValue(), doc); + } + }, + }; - public static IOpenApiSchema LoadSchema(ParseNode node, OpenApiDocument hostDocument) - { - var mapNode = node.CheckMapNode(OpenApiConstants.Schema); + private static readonly PatternFieldMap _openApiSchemaPatternFields = new() + { + {s => s.StartsWith(OpenApiConstants.ExtensionFieldNamePrefix, StringComparison.OrdinalIgnoreCase), (o, p, n, _) => o.AddExtension(p, LoadExtension(p,n))} + }; - var pointer = mapNode.GetReferencePointer(); - var identifier = mapNode.GetJsonSchemaIdentifier(); - var nodeLocation = node.Context.GetLocation(); + public static IOpenApiSchema LoadSchema(ParseNode node, OpenApiDocument hostDocument) + { + var mapNode = node.CheckMapNode(OpenApiConstants.Schema); - if (pointer != null) - { - var reference = GetReferenceIdAndExternalResource(pointer); - var result = new OpenApiSchemaReference(reference.Item1, hostDocument, reference.Item2); - result.Reference.SetMetadataFromMapNode(mapNode); - result.Reference.SetJsonPointerPath(pointer, nodeLocation); - return result; - } + var pointer = mapNode.GetReferencePointer(); + var identifier = mapNode.GetJsonSchemaIdentifier(); + var nodeLocation = node.Context.GetLocation(); - var schema = new OpenApiSchema(); + if (pointer != null) + { + var reference = GetReferenceIdAndExternalResource(pointer); + var result = new OpenApiSchemaReference(reference.Item1, hostDocument, reference.Item2); + result.Reference.SetMetadataFromMapNode(mapNode); + result.Reference.SetJsonPointerPath(pointer, nodeLocation); + return result; + } - foreach (var propertyNode in mapNode) - { - bool isRecognized = _openApiSchemaFixedFields.ContainsKey(propertyNode.Name) || - _openApiSchemaPatternFields.Any(p => p.Key(propertyNode.Name)); + var schema = new OpenApiSchema(); - if (isRecognized) - { - propertyNode.ParseField(schema, _openApiSchemaFixedFields, _openApiSchemaPatternFields, hostDocument); - } - else if (propertyNode.JsonNode is not null) - { - schema.UnrecognizedKeywords ??= new Dictionary(StringComparer.Ordinal); - schema.UnrecognizedKeywords[propertyNode.Name] = propertyNode.JsonNode; - } - } + foreach (var propertyNode in mapNode) + { + bool isRecognized = _openApiSchemaFixedFields.ContainsKey(propertyNode.Name) || + _openApiSchemaPatternFields.Any(p => p.Key(propertyNode.Name)); - if (schema.Extensions is not null && schema.Extensions.ContainsKey(OpenApiConstants.NullableExtension)) + if (isRecognized) { - if (schema.Type.HasValue) - schema.Type |= JsonSchemaType.Null; - else - schema.Type = JsonSchemaType.Null; - - schema.Extensions.Remove(OpenApiConstants.NullableExtension); + propertyNode.ParseField(schema, _openApiSchemaFixedFields, _openApiSchemaPatternFields, hostDocument); } - - if (!string.IsNullOrEmpty(identifier) && hostDocument.Workspace is not null) + else if (propertyNode.JsonNode is not null) { - // register the schema in our registry using the identifier's URL - hostDocument.Workspace.RegisterComponentForDocument(hostDocument, schema, identifier!); + schema.UnrecognizedKeywords ??= new Dictionary(StringComparer.Ordinal); + schema.UnrecognizedKeywords[propertyNode.Name] = propertyNode.JsonNode; } + } - return schema; + if (schema.Extensions is not null && schema.Extensions.ContainsKey(OpenApiConstants.NullableExtension)) + { + if (schema.Type.HasValue) + schema.Type |= JsonSchemaType.Null; + else + schema.Type = JsonSchemaType.Null; + + schema.Extensions.Remove(OpenApiConstants.NullableExtension); } + + if (!string.IsNullOrEmpty(identifier) && hostDocument.Workspace is not null) + { + // register the schema in our registry using the identifier's URL + hostDocument.Workspace.RegisterComponentForDocument(hostDocument, schema, identifier!); + } + + return schema; } }