Skip to content

Commit 8c9047f

Browse files
committed
Implement helper methods for switching between enum types and their equivalent identifiers
1 parent 79f3e94 commit 8c9047f

File tree

8 files changed

+101
-54
lines changed

8 files changed

+101
-54
lines changed

src/Microsoft.OpenApi/Extensions/OpenApiTypeMapper.cs

Lines changed: 72 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -12,40 +12,81 @@ namespace Microsoft.OpenApi.Extensions
1212
/// </summary>
1313
public static class OpenApiTypeMapper
1414
{
15+
/// <summary>
16+
/// Maps a JsonSchema data type to an identifier.
17+
/// </summary>
18+
/// <param name="schemaType"></param>
19+
/// <returns></returns>
20+
public static string ToIdentifier(JsonSchemaType? schemaType)
21+
{
22+
return schemaType switch
23+
{
24+
JsonSchemaType.Null => "null",
25+
JsonSchemaType.Boolean => "boolean",
26+
JsonSchemaType.Integer => "integer",
27+
JsonSchemaType.Number => "number",
28+
JsonSchemaType.String => "string",
29+
JsonSchemaType.Array => "array",
30+
JsonSchemaType.Object => "object",
31+
_ => null,
32+
};
33+
}
34+
35+
/// <summary>
36+
/// Converts a schema type's identifier into the enum equivalent
37+
/// </summary>
38+
/// <param name="identifier"></param>
39+
/// <returns></returns>
40+
public static JsonSchemaType IdentifierToEnumType(string identifier)
41+
{
42+
return identifier switch
43+
{
44+
"null" => JsonSchemaType.Null,
45+
"boolean" => JsonSchemaType.Boolean,
46+
"integer" or "int" => JsonSchemaType.Integer,
47+
"number" or "double" => JsonSchemaType.Number,
48+
"string" => JsonSchemaType.String,
49+
"array" => JsonSchemaType.Array,
50+
"object" => JsonSchemaType.Object,
51+
"file" => JsonSchemaType.String, // File is treated as string
52+
_ => JsonSchemaType.Any,
53+
};
54+
}
55+
1556
private static readonly Dictionary<Type, Func<OpenApiSchema>> _simpleTypeToOpenApiSchema = new()
1657
{
17-
[typeof(bool)] = () => new() { Type = "boolean" },
18-
[typeof(byte)] = () => new() { Type = "string", Format = "byte" },
19-
[typeof(int)] = () => new() { Type = "integer", Format = "int32" },
20-
[typeof(uint)] = () => new() { Type = "integer", Format = "int32" },
21-
[typeof(long)] = () => new() { Type = "integer", Format = "int64" },
22-
[typeof(ulong)] = () => new() { Type = "integer", Format = "int64" },
23-
[typeof(float)] = () => new() { Type = "number", Format = "float" },
24-
[typeof(double)] = () => new() { Type = "number", Format = "double" },
25-
[typeof(decimal)] = () => new() { Type = "number", Format = "double" },
26-
[typeof(DateTime)] = () => new() { Type = "string", Format = "date-time" },
27-
[typeof(DateTimeOffset)] = () => new() { Type = "string", Format = "date-time" },
28-
[typeof(Guid)] = () => new() { Type = "string", Format = "uuid" },
29-
[typeof(char)] = () => new() { Type = "string" },
58+
[typeof(bool)] = () => new() { Type = JsonSchemaType.Boolean },
59+
[typeof(byte)] = () => new() { Type = JsonSchemaType.String, Format = "byte" },
60+
[typeof(int)] = () => new() { Type = JsonSchemaType.Integer, Format = "int32" },
61+
[typeof(uint)] = () => new() { Type = JsonSchemaType.Integer, Format = "int32" },
62+
[typeof(long)] = () => new() { Type = JsonSchemaType.Integer, Format = "int64" },
63+
[typeof(ulong)] = () => new() { Type = JsonSchemaType.Integer, Format = "int64" },
64+
[typeof(float)] = () => new() { Type = JsonSchemaType.Number, Format = "float" },
65+
[typeof(double)] = () => new() { Type = JsonSchemaType.Number, Format = "double" },
66+
[typeof(decimal)] = () => new() { Type = JsonSchemaType.Number, Format = "double" },
67+
[typeof(DateTime)] = () => new() { Type = JsonSchemaType.String, Format = "date-time" },
68+
[typeof(DateTimeOffset)] = () => new() { Type = JsonSchemaType.String, Format = "date-time" },
69+
[typeof(Guid)] = () => new() { Type = JsonSchemaType.String, Format = "uuid" },
70+
[typeof(char)] = () => new() { Type = JsonSchemaType.String },
3071

3172
// Nullable types
32-
[typeof(bool?)] = () => new() { Type = "boolean", Nullable = true },
33-
[typeof(byte?)] = () => new() { Type = "string", Format = "byte", Nullable = true },
34-
[typeof(int?)] = () => new() { Type = "integer", Format = "int32", Nullable = true },
35-
[typeof(uint?)] = () => new() { Type = "integer", Format = "int32", Nullable = true },
36-
[typeof(long?)] = () => new() { Type = "integer", Format = "int64", Nullable = true },
37-
[typeof(ulong?)] = () => new() { Type = "integer", Format = "int64", Nullable = true },
38-
[typeof(float?)] = () => new() { Type = "number", Format = "float", Nullable = true },
39-
[typeof(double?)] = () => new() { Type = "number", Format = "double", Nullable = true },
40-
[typeof(decimal?)] = () => new() { Type = "number", Format = "double", Nullable = true },
41-
[typeof(DateTime?)] = () => new() { Type = "string", Format = "date-time", Nullable = true },
42-
[typeof(DateTimeOffset?)] = () => new() { Type = "string", Format = "date-time", Nullable = true },
43-
[typeof(Guid?)] = () => new() { Type = "string", Format = "uuid", Nullable = true },
44-
[typeof(char?)] = () => new() { Type = "string", Nullable = true },
73+
[typeof(bool?)] = () => new() { Type = JsonSchemaType.Boolean, Nullable = true },
74+
[typeof(byte?)] = () => new() { Type = JsonSchemaType.String, Format = "byte", Nullable = true },
75+
[typeof(int?)] = () => new() { Type = JsonSchemaType.Integer, Format = "int32", Nullable = true },
76+
[typeof(uint?)] = () => new() { Type = JsonSchemaType.Integer, Format = "int32", Nullable = true },
77+
[typeof(long?)] = () => new() { Type = JsonSchemaType.Integer, Format = "int64", Nullable = true },
78+
[typeof(ulong?)] = () => new() { Type = JsonSchemaType.Integer, Format = "int64", Nullable = true },
79+
[typeof(float?)] = () => new() { Type = JsonSchemaType.Number, Format = "float", Nullable = true },
80+
[typeof(double?)] = () => new() { Type = JsonSchemaType.Number, Format = "double", Nullable = true },
81+
[typeof(decimal?)] = () => new() { Type = JsonSchemaType.Number, Format = "double", Nullable = true },
82+
[typeof(DateTime?)] = () => new() { Type = JsonSchemaType.String, Format = "date-time", Nullable = true },
83+
[typeof(DateTimeOffset?)] = () => new() { Type = JsonSchemaType.String, Format = "date-time", Nullable = true },
84+
[typeof(Guid?)] = () => new() { Type = JsonSchemaType.String, Format = "uuid", Nullable = true },
85+
[typeof(char?)] = () => new() { Type = JsonSchemaType.String, Nullable = true },
4586

46-
[typeof(Uri)] = () => new() { Type = "string", Format = "uri" }, // Uri is treated as simple string
47-
[typeof(string)] = () => new() { Type = "string" },
48-
[typeof(object)] = () => new() { Type = "object" }
87+
[typeof(Uri)] = () => new() { Type = JsonSchemaType.String, Format = "uri" }, // Uri is treated as simple string
88+
[typeof(string)] = () => new() { Type = JsonSchemaType.String },
89+
[typeof(object)] = () => new() { Type = JsonSchemaType.Object }
4990
};
5091

5192
/// <summary>
@@ -79,7 +120,7 @@ public static OpenApiSchema MapTypeToOpenApiPrimitiveType(this Type type)
79120

80121
return _simpleTypeToOpenApiSchema.TryGetValue(type, out var result)
81122
? result()
82-
: new() { Type = "string" };
123+
: new() { Type = JsonSchemaType.String };
83124
}
84125

85126
/// <summary>
@@ -95,7 +136,7 @@ public static Type MapOpenApiPrimitiveTypeToSimpleType(this OpenApiSchema schema
95136
throw new ArgumentNullException(nameof(schema));
96137
}
97138

98-
var type = (schema.Type?.ToString().ToLowerInvariant(), schema.Format?.ToLowerInvariant(), schema.Nullable) switch
139+
var type = (ToIdentifier(schema.Type), schema.Format?.ToLowerInvariant(), schema.Nullable) switch
99140
{
100141
("boolean", null, false) => typeof(bool),
101142
("integer", "int32", false) => typeof(int),

src/Microsoft.OpenApi/Models/OpenApiRequestBody.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
using System.Collections.Generic;
66
using System.Linq;
77
using Microsoft.OpenApi.Any;
8+
using Microsoft.OpenApi.Extensions;
89
using Microsoft.OpenApi.Interfaces;
910
using Microsoft.OpenApi.Writers;
1011

@@ -141,11 +142,11 @@ internal IEnumerable<OpenApiFormDataParameter> ConvertToFormDataParameters()
141142
foreach (var property in Content.First().Value.Schema.Properties)
142143
{
143144
var paramSchema = property.Value;
144-
if ("string".Equals(paramSchema.Type.ToString(), StringComparison.OrdinalIgnoreCase)
145+
if ("string".Equals(OpenApiTypeMapper.ToIdentifier(paramSchema.Type), StringComparison.OrdinalIgnoreCase)
145146
&& ("binary".Equals(paramSchema.Format, StringComparison.OrdinalIgnoreCase)
146147
|| "base64".Equals(paramSchema.Format, StringComparison.OrdinalIgnoreCase)))
147148
{
148-
paramSchema.Type = "file";
149+
paramSchema.Type = OpenApiTypeMapper.IdentifierToEnumType("file");
149150
paramSchema.Format = null;
150151
}
151152
yield return new()

src/Microsoft.OpenApi/Reader/V2/OpenApiHeaderDeserializer.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright (c) Microsoft Corporation. All rights reserved.
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
22
// Licensed under the MIT license.
33

44
using System;
@@ -24,7 +24,7 @@ internal static partial class OpenApiV2Deserializer
2424
},
2525
{
2626
"type",
27-
(o, n, _) => GetOrCreateSchema(o).Type = n.GetScalarValue()
27+
(o, n, _) => GetOrCreateSchema(o).Type = OpenApiTypeMapper.IdentifierToEnumType(n.GetScalarValue())
2828
},
2929
{
3030
"format",

src/Microsoft.OpenApi/Reader/V2/OpenApiParameterDeserializer.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright (c) Microsoft Corporation. All rights reserved.
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
22
// Licensed under the MIT license.
33

44
using System;
@@ -46,7 +46,7 @@ internal static partial class OpenApiV2Deserializer
4646
},
4747
{
4848
"type",
49-
(o, n, t) => GetOrCreateSchema(o).Type = n.GetScalarValue()
49+
(o, n, t) => GetOrCreateSchema(o).Type = OpenApiTypeMapper.IdentifierToEnumType(n.GetScalarValue())
5050
},
5151
{
5252
"items",

src/Microsoft.OpenApi/Reader/V2/OpenApiSchemaDeserializer.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ internal static partial class OpenApiV2Deserializer
8585

8686
{
8787
"type",
88-
(o, n, _) => o.Type = n.GetScalarValue()
88+
(o, n, _) => o.Type = OpenApiTypeMapper.IdentifierToEnumType(n.GetScalarValue())
8989
},
9090
{
9191
"allOf",

src/Microsoft.OpenApi/Reader/V3/OpenApiSchemaDeserializer.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ internal static partial class OpenApiV3Deserializer
8484
},
8585
{
8686
"type",
87-
(o, n, _) => o.Type = n.GetScalarValue()
87+
(o, n, _) => o.Type = OpenApiTypeMapper.IdentifierToEnumType(n.GetScalarValue())
8888
},
8989
{
9090
"allOf",

src/Microsoft.OpenApi/Reader/V31/OpenApiSchemaDeserializer.cs

Lines changed: 18 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -122,9 +122,21 @@ internal static partial class OpenApiV31Deserializer
122122
"type",
123123
(o, n, _) =>
124124
{
125-
o.Type = n is ValueNode
126-
? n.GetScalarValue()
127-
: n.CreateSimpleList((n2, p) => n2.GetScalarValue()).ToArray();
125+
if (n is ValueNode)
126+
{
127+
o.Type = OpenApiTypeMapper.IdentifierToEnumType(n.GetScalarValue());
128+
}
129+
else
130+
{
131+
var list = n.CreateSimpleList((n2, p) => n2.GetScalarValue());
132+
JsonSchemaType combinedType = JsonSchemaType.Any;
133+
foreach(var type in list)
134+
{
135+
var schemaType = OpenApiTypeMapper.IdentifierToEnumType(type);
136+
combinedType |= schemaType;
137+
}
138+
o.Type = combinedType;
139+
}
128140
}
129141
},
130142
{
@@ -187,15 +199,7 @@ internal static partial class OpenApiV31Deserializer
187199
var nullable = bool.Parse(n.GetScalarValue());
188200
if (nullable) // if nullable, convert type into an array of type(s) and null
189201
{
190-
if (o.Type is string[] typeArray)
191-
{
192-
var typeList = new List<string>(typeArray) { OpenApiConstants.Null };
193-
o.Type = typeList.ToArray();
194-
}
195-
else if (o.Type is string typeString)
196-
{
197-
o.Type = new string[]{typeString, OpenApiConstants.Null};
198-
}
202+
o.Type |= JsonSchemaType.Null;
199203
}
200204
}
201205
},
@@ -259,8 +263,8 @@ public static OpenApiSchema LoadSchema(ParseNode node, OpenApiDocument hostDocum
259263

260264
if (schema.Extensions.ContainsKey(OpenApiConstants.NullableExtension))
261265
{
262-
var type = schema.Type;
263-
schema.Type = new string[] {(string)type, OpenApiConstants.Null};
266+
var type = schema.Type;
267+
schema.Type = type | JsonSchemaType.Null;
264268
schema.Extensions.Remove(OpenApiConstants.NullableExtension);
265269
}
266270

src/Microsoft.OpenApi/Validations/Rules/RuleHelpers.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
using System.Text.Json;
66
using System.Text.Json.Nodes;
77
using Microsoft.OpenApi.Any;
8+
using Microsoft.OpenApi.Extensions;
89
using Microsoft.OpenApi.Models;
910

1011
namespace Microsoft.OpenApi.Validations.Rules
@@ -55,7 +56,7 @@ public static void ValidateDataTypeMismatch(
5556
// convert value to JsonElement and access the ValueKind property to determine the type.
5657
var jsonElement = JsonDocument.Parse(JsonSerializer.Serialize(value)).RootElement;
5758

58-
var type = (string)schema.Type;
59+
var type = OpenApiTypeMapper.ToIdentifier(schema.Type);
5960
var format = schema.Format;
6061
var nullable = schema.Nullable;
6162

0 commit comments

Comments
 (0)