6
6
using System . Linq ;
7
7
using System . Text ;
8
8
using Microsoft . OpenApi . Any ;
9
- using Microsoft . OpenApi . Exceptions ;
10
9
using Microsoft . OpenApi . Models ;
11
10
12
11
namespace Microsoft . OpenApi . Readers . ParseNodes
13
12
{
14
13
internal static class OpenApiAnyConverter
15
14
{
16
- /// <summary>
17
- /// Converts the <see cref="OpenApiString"/>s in the given <see cref="IOpenApiAny"/>
18
- /// into the most specific <see cref="IOpenApiPrimitive"/> type based on the value.
19
- /// </summary>
20
- public static IOpenApiAny GetSpecificOpenApiAny ( IOpenApiAny openApiAny )
21
- {
22
- if ( openApiAny is OpenApiArray openApiArray )
23
- {
24
- var newArray = new OpenApiArray ( ) ;
25
- foreach ( var element in openApiArray )
26
- {
27
- newArray . Add ( GetSpecificOpenApiAny ( element ) ) ;
28
- }
29
-
30
- return newArray ;
31
- }
32
-
33
- if ( openApiAny is OpenApiObject openApiObject )
34
- {
35
- var newObject = new OpenApiObject ( ) ;
36
-
37
- foreach ( var key in openApiObject . Keys . ToList ( ) )
38
- {
39
- newObject [ key ] = GetSpecificOpenApiAny ( openApiObject [ key ] ) ;
40
- }
41
-
42
- return newObject ;
43
- }
44
-
45
- if ( ! ( openApiAny is OpenApiString ) )
46
- {
47
- return openApiAny ;
48
- }
49
-
50
- var value = ( ( OpenApiString ) openApiAny ) . Value ;
51
-
52
- if ( value == null || value == "null" )
53
- {
54
- return new OpenApiNull ( ) ;
55
- }
56
-
57
- if ( value == "true" )
58
- {
59
- return new OpenApiBoolean ( true ) ;
60
- }
61
-
62
- if ( value == "false" )
63
- {
64
- return new OpenApiBoolean ( false ) ;
65
- }
66
-
67
- if ( int . TryParse ( value , NumberStyles . Integer , CultureInfo . InvariantCulture , out var intValue ) )
68
- {
69
- return new OpenApiInteger ( intValue ) ;
70
- }
71
-
72
- if ( long . TryParse ( value , NumberStyles . Integer , CultureInfo . InvariantCulture , out var longValue ) )
73
- {
74
- return new OpenApiLong ( longValue ) ;
75
- }
76
-
77
- if ( double . TryParse ( value , NumberStyles . Float | NumberStyles . AllowThousands , CultureInfo . InvariantCulture , out var doubleValue ) )
78
- {
79
- return new OpenApiDouble ( doubleValue ) ;
80
- }
81
-
82
- if ( DateTimeOffset . TryParse ( value , CultureInfo . InvariantCulture , DateTimeStyles . None , out var dateTimeValue ) )
83
- {
84
- return new OpenApiDateTime ( dateTimeValue ) ;
85
- }
86
-
87
- // if we can't identify the type of value, return it as string.
88
- return new OpenApiString ( value ) ;
89
- }
90
-
91
15
/// <summary>
92
16
/// Converts the <see cref="OpenApiString"/>s in the given <see cref="IOpenApiAny"/>
93
17
/// into the appropriate <see cref="IOpenApiPrimitive"/> type based on the given <see cref="OpenApiSchema"/>.
94
18
/// For those strings that the schema does not specify the type for, convert them into
95
19
/// the most specific type based on the value.
96
20
/// </summary>
97
- public static IOpenApiAny GetSpecificOpenApiAny ( IOpenApiAny openApiAny , OpenApiSchema schema )
21
+ public static IOpenApiAny GetSpecificOpenApiAny ( IOpenApiAny openApiAny , OpenApiSchema schema = null )
98
22
{
99
23
if ( openApiAny is OpenApiArray openApiArray )
100
24
{
@@ -113,9 +37,9 @@ public static IOpenApiAny GetSpecificOpenApiAny(IOpenApiAny openApiAny, OpenApiS
113
37
114
38
foreach ( var key in openApiObject . Keys . ToList ( ) )
115
39
{
116
- if ( schema != null && schema . Properties != null && schema . Properties . ContainsKey ( key ) )
40
+ if ( schema ? . Properties != null && schema . Properties . TryGetValue ( key , out var property ) )
117
41
{
118
- newObject [ key ] = GetSpecificOpenApiAny ( openApiObject [ key ] , schema . Properties [ key ] ) ;
42
+ newObject [ key ] = GetSpecificOpenApiAny ( openApiObject [ key ] , property ) ;
119
43
}
120
44
else
121
45
{
@@ -131,128 +55,169 @@ public static IOpenApiAny GetSpecificOpenApiAny(IOpenApiAny openApiAny, OpenApiS
131
55
return openApiAny ;
132
56
}
133
57
134
- if ( schema ? . Type == null )
135
- {
136
- return GetSpecificOpenApiAny ( openApiAny ) ;
137
- }
58
+ var value = ( ( OpenApiString ) openApiAny ) . Value ;
138
59
139
- var type = schema . Type ;
140
- var format = schema . Format ;
60
+ // For explicit strings only try to guess if it's a DateTimeOffset
61
+ if ( ( ( OpenApiString ) openApiAny ) . IsExplicit ( ) )
62
+ {
63
+ if ( DateTimeOffset . TryParse ( value , CultureInfo . InvariantCulture , DateTimeStyles . None , out var dateTimeValue ) )
64
+ {
65
+ return new OpenApiDateTime ( dateTimeValue ) ;
66
+ }
141
67
142
- var value = ( ( OpenApiString ) openApiAny ) . Value ;
68
+ return openApiAny ;
69
+ }
143
70
144
71
if ( value == null || value == "null" )
145
72
{
146
73
return new OpenApiNull ( ) ;
147
74
}
148
75
149
- if ( type == "integer" && format == "int32" )
76
+ if ( schema ? . Type == null )
150
77
{
151
- if ( int . TryParse ( value , NumberStyles . Integer , CultureInfo . InvariantCulture , out var intValue ) )
78
+ if ( value == "true" )
152
79
{
153
- return new OpenApiInteger ( intValue ) ;
80
+ return new OpenApiBoolean ( true ) ;
154
81
}
155
- }
156
82
157
- if ( type == "integer" && format == "int64" )
158
- {
159
- if ( long . TryParse ( value , NumberStyles . Integer , CultureInfo . InvariantCulture , out var longValue ) )
83
+ if ( value == "false" )
160
84
{
161
- return new OpenApiLong ( longValue ) ;
85
+ return new OpenApiBoolean ( false ) ;
162
86
}
163
- }
164
87
165
- if ( type == "integer" )
166
- {
167
88
if ( int . TryParse ( value , NumberStyles . Integer , CultureInfo . InvariantCulture , out var intValue ) )
168
89
{
169
90
return new OpenApiInteger ( intValue ) ;
170
91
}
171
- }
172
92
173
- if ( type == "number" && format == "float" )
174
- {
175
- if ( float . TryParse ( value , NumberStyles . Float | NumberStyles . AllowThousands , CultureInfo . InvariantCulture , out var floatValue ) )
93
+ if ( long . TryParse ( value , NumberStyles . Integer , CultureInfo . InvariantCulture , out var longValue ) )
176
94
{
177
- return new OpenApiFloat ( floatValue ) ;
95
+ return new OpenApiLong ( longValue ) ;
178
96
}
179
- }
180
97
181
- if ( type == "number" && format == "double" )
182
- {
183
98
if ( double . TryParse ( value , NumberStyles . Float | NumberStyles . AllowThousands , CultureInfo . InvariantCulture , out var doubleValue ) )
184
99
{
185
100
return new OpenApiDouble ( doubleValue ) ;
186
101
}
187
- }
188
102
189
- if ( type == "number" )
190
- {
191
- if ( double . TryParse ( value , NumberStyles . Float | NumberStyles . AllowThousands , CultureInfo . InvariantCulture , out var doubleValue ) )
103
+ if ( DateTimeOffset . TryParse ( value , CultureInfo . InvariantCulture , DateTimeStyles . None , out var dateTimeValue ) )
192
104
{
193
- return new OpenApiDouble ( doubleValue ) ;
105
+ return new OpenApiDateTime ( dateTimeValue ) ;
194
106
}
195
107
}
196
-
197
- if ( type == "string" && format == "byte" )
108
+ else
198
109
{
199
- try
110
+ var type = schema . Type ;
111
+ var format = schema . Format ;
112
+
113
+ if ( type == "integer" && format == "int32" )
200
114
{
201
- return new OpenApiByte ( Convert . FromBase64String ( value ) ) ;
115
+ if ( int . TryParse ( value , NumberStyles . Integer , CultureInfo . InvariantCulture , out var intValue ) )
116
+ {
117
+ return new OpenApiInteger ( intValue ) ;
118
+ }
202
119
}
203
- catch ( FormatException )
204
- { }
205
- }
206
120
207
- // binary
208
- if ( type == "string" && format == "binary" )
209
- {
210
- try
121
+ if ( type == "integer" && format == "int64" )
211
122
{
212
- return new OpenApiBinary ( Encoding . UTF8 . GetBytes ( value ) ) ;
123
+ if ( long . TryParse ( value , NumberStyles . Integer , CultureInfo . InvariantCulture , out var longValue ) )
124
+ {
125
+ return new OpenApiLong ( longValue ) ;
126
+ }
213
127
}
214
- catch ( EncoderFallbackException )
215
- { }
216
- }
217
128
218
- if ( type == "string" && format == "date" )
219
- {
220
- if ( DateTimeOffset . TryParse ( value , CultureInfo . InvariantCulture , DateTimeStyles . None , out var dateValue ) )
129
+ if ( type == "integer" )
221
130
{
222
- return new OpenApiDate ( dateValue . Date ) ;
131
+ if ( int . TryParse ( value , NumberStyles . Integer , CultureInfo . InvariantCulture , out var intValue ) )
132
+ {
133
+ return new OpenApiInteger ( intValue ) ;
134
+ }
223
135
}
224
- }
225
136
226
- if ( type == "string" && format == "date-time" )
227
- {
228
- if ( DateTimeOffset . TryParse ( value , CultureInfo . InvariantCulture , DateTimeStyles . None , out var dateTimeValue ) )
137
+ if ( type == "number" && format == "float" )
229
138
{
230
- return new OpenApiDateTime ( dateTimeValue ) ;
139
+ if ( float . TryParse ( value , NumberStyles . Float | NumberStyles . AllowThousands , CultureInfo . InvariantCulture , out var floatValue ) )
140
+ {
141
+ return new OpenApiFloat ( floatValue ) ;
142
+ }
231
143
}
232
- }
233
144
234
- if ( type == "string" && format == "password" )
235
- {
236
- return new OpenApiPassword ( value ) ;
237
- }
145
+ if ( type == "number" && format == "double" )
146
+ {
147
+ if ( double . TryParse ( value , NumberStyles . Float | NumberStyles . AllowThousands , CultureInfo . InvariantCulture , out var doubleValue ) )
148
+ {
149
+ return new OpenApiDouble ( doubleValue ) ;
150
+ }
151
+ }
238
152
239
- if ( type == "string" )
240
- {
241
- return new OpenApiString ( value ) ;
242
- }
153
+ if ( type == "number" )
154
+ {
155
+ if ( double . TryParse ( value , NumberStyles . Float | NumberStyles . AllowThousands , CultureInfo . InvariantCulture , out var doubleValue ) )
156
+ {
157
+ return new OpenApiDouble ( doubleValue ) ;
158
+ }
159
+ }
243
160
244
- if ( type == "boolean" )
245
- {
246
- if ( bool . TryParse ( value , out var booleanValue ) )
161
+ if ( type == "string" && format == "byte" )
162
+ {
163
+ try
164
+ {
165
+ return new OpenApiByte ( Convert . FromBase64String ( value ) ) ;
166
+ }
167
+ catch ( FormatException )
168
+ { }
169
+ }
170
+
171
+ // binary
172
+ if ( type == "string" && format == "binary" )
173
+ {
174
+ try
175
+ {
176
+ return new OpenApiBinary ( Encoding . UTF8 . GetBytes ( value ) ) ;
177
+ }
178
+ catch ( EncoderFallbackException )
179
+ { }
180
+ }
181
+
182
+ if ( type == "string" && format == "date" )
247
183
{
248
- return new OpenApiBoolean ( booleanValue ) ;
184
+ if ( DateTimeOffset . TryParse ( value , CultureInfo . InvariantCulture , DateTimeStyles . None , out var dateValue ) )
185
+ {
186
+ return new OpenApiDate ( dateValue . Date ) ;
187
+ }
188
+ }
189
+
190
+ if ( type == "string" && format == "date-time" )
191
+ {
192
+ if ( DateTimeOffset . TryParse ( value , CultureInfo . InvariantCulture , DateTimeStyles . None , out var dateTimeValue ) )
193
+ {
194
+ return new OpenApiDateTime ( dateTimeValue ) ;
195
+ }
196
+ }
197
+
198
+ if ( type == "string" && format == "password" )
199
+ {
200
+ return new OpenApiPassword ( value ) ;
201
+ }
202
+
203
+ if ( type == "string" )
204
+ {
205
+ return new OpenApiString ( value ) ;
206
+ }
207
+
208
+ if ( type == "boolean" )
209
+ {
210
+ if ( bool . TryParse ( value , out var booleanValue ) )
211
+ {
212
+ return new OpenApiBoolean ( booleanValue ) ;
213
+ }
249
214
}
250
215
}
251
216
252
217
// If data conflicts with the given type, return a string.
253
218
// This converter is used in the parser, so it does not perform any validations,
254
219
// but the validator can be used to validate whether the data and given type conflicts.
255
- return new OpenApiString ( value ) ;
220
+ return openApiAny ;
256
221
}
257
222
}
258
223
}
0 commit comments