Skip to content

Commit 1a8cdc9

Browse files
committed
Parse decimals and ints with CultureInfo.InvariantCulture since JSON/YAML doesn't vary by culture.
For example, the parser should always treat 100.000 as one hundred, not one hundred thousand, even if the operating system on the machine is set to use French.
1 parent 9aa3e97 commit 1a8cdc9

File tree

9 files changed

+189
-35
lines changed

9 files changed

+189
-35
lines changed

src/Microsoft.OpenApi.Readers/V2/OpenApiDocumentDeserializer.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
using System;
55
using System.Collections.Generic;
6+
using System.Globalization;
67
using System.Linq;
78
using Microsoft.OpenApi.Extensions;
89
using Microsoft.OpenApi.Interfaces;
@@ -191,7 +192,7 @@ private static string BuildUrl(string scheme, string host, string basePath)
191192
{
192193
var pieces = host.Split(':');
193194
host = pieces.First();
194-
port = int.Parse(pieces.Last());
195+
port = int.Parse(pieces.Last(), CultureInfo.InvariantCulture);
195196
}
196197

197198
var uriBuilder = new UriBuilder()

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

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// Licensed under the MIT license.
33

44
using System;
5+
using System.Globalization;
56
using Microsoft.OpenApi.Any;
67
using Microsoft.OpenApi.Extensions;
78
using Microsoft.OpenApi.Models;
@@ -54,10 +55,10 @@ internal static partial class OpenApiV2Deserializer
5455
GetOrCreateSchema(o).Default = n.CreateAny();
5556
}
5657
},
57-
{
58+
{
5859
"maximum", (o, n) =>
5960
{
60-
GetOrCreateSchema(o).Maximum = decimal.Parse(n.GetScalarValue());
61+
GetOrCreateSchema(o).Maximum = decimal.Parse(n.GetScalarValue(), CultureInfo.InvariantCulture);
6162
}
6263
},
6364
{
@@ -69,7 +70,7 @@ internal static partial class OpenApiV2Deserializer
6970
{
7071
"minimum", (o, n) =>
7172
{
72-
GetOrCreateSchema(o).Minimum = decimal.Parse(n.GetScalarValue());
73+
GetOrCreateSchema(o).Minimum = decimal.Parse(n.GetScalarValue(), CultureInfo.InvariantCulture);
7374
}
7475
},
7576
{
@@ -81,13 +82,13 @@ internal static partial class OpenApiV2Deserializer
8182
{
8283
"maxLength", (o, n) =>
8384
{
84-
GetOrCreateSchema(o).MaxLength = int.Parse(n.GetScalarValue());
85+
GetOrCreateSchema(o).MaxLength = int.Parse(n.GetScalarValue(), CultureInfo.InvariantCulture);
8586
}
8687
},
8788
{
8889
"minLength", (o, n) =>
8990
{
90-
GetOrCreateSchema(o).MinLength = int.Parse(n.GetScalarValue());
91+
GetOrCreateSchema(o).MinLength = int.Parse(n.GetScalarValue(), CultureInfo.InvariantCulture);
9192
}
9293
},
9394
{
@@ -99,13 +100,13 @@ internal static partial class OpenApiV2Deserializer
99100
{
100101
"maxItems", (o, n) =>
101102
{
102-
GetOrCreateSchema(o).MaxItems = int.Parse(n.GetScalarValue());
103+
GetOrCreateSchema(o).MaxItems = int.Parse(n.GetScalarValue(), CultureInfo.InvariantCulture);
103104
}
104105
},
105106
{
106107
"minItems", (o, n) =>
107108
{
108-
GetOrCreateSchema(o).MinItems = int.Parse(n.GetScalarValue());
109+
GetOrCreateSchema(o).MinItems = int.Parse(n.GetScalarValue(), CultureInfo.InvariantCulture);
109110
}
110111
},
111112
{
@@ -117,7 +118,7 @@ internal static partial class OpenApiV2Deserializer
117118
{
118119
"multipleOf", (o, n) =>
119120
{
120-
GetOrCreateSchema(o).MultipleOf = decimal.Parse(n.GetScalarValue());
121+
GetOrCreateSchema(o).MultipleOf = decimal.Parse(n.GetScalarValue(), CultureInfo.InvariantCulture);
121122
}
122123
},
123124
{

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

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
using System;
55
using System.Collections.Generic;
6+
using System.Globalization;
67
using Microsoft.OpenApi.Any;
78
using Microsoft.OpenApi.Extensions;
89
using Microsoft.OpenApi.Models;
@@ -90,25 +91,25 @@ internal static partial class OpenApiV2Deserializer
9091
{
9192
"minimum", (o, n) =>
9293
{
93-
GetOrCreateSchema(o).Minimum = decimal.Parse(n.GetScalarValue());
94+
GetOrCreateSchema(o).Minimum = decimal.Parse(n.GetScalarValue(), CultureInfo.InvariantCulture);
9495
}
9596
},
9697
{
9798
"maximum", (o, n) =>
9899
{
99-
GetOrCreateSchema(o).Maximum = decimal.Parse(n.GetScalarValue());
100+
GetOrCreateSchema(o).Maximum = decimal.Parse(n.GetScalarValue(), CultureInfo.InvariantCulture);
100101
}
101102
},
102103
{
103104
"maxLength", (o, n) =>
104105
{
105-
GetOrCreateSchema(o).MaxLength = int.Parse(n.GetScalarValue());
106+
GetOrCreateSchema(o).MaxLength = int.Parse(n.GetScalarValue(), CultureInfo.InvariantCulture);
106107
}
107108
},
108109
{
109110
"minLength", (o, n) =>
110111
{
111-
GetOrCreateSchema(o).MinLength = int.Parse(n.GetScalarValue());
112+
GetOrCreateSchema(o).MinLength = int.Parse(n.GetScalarValue(), CultureInfo.InvariantCulture);
112113
}
113114
},
114115
{

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

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
using Microsoft.OpenApi.Models;
77
using Microsoft.OpenApi.Readers.ParseNodes;
88
using System.Collections.Generic;
9+
using System.Globalization;
910

1011
namespace Microsoft.OpenApi.Readers.V2
1112
{
@@ -26,13 +27,13 @@ internal static partial class OpenApiV2Deserializer
2627
{
2728
"multipleOf", (o, n) =>
2829
{
29-
o.MultipleOf = decimal.Parse(n.GetScalarValue());
30+
o.MultipleOf = decimal.Parse(n.GetScalarValue(), CultureInfo.InvariantCulture);
3031
}
3132
},
3233
{
3334
"maximum", (o, n) =>
3435
{
35-
o.Maximum = decimal.Parse(n.GetScalarValue());
36+
o.Maximum = decimal.Parse(n.GetScalarValue(), CultureInfo.InvariantCulture);
3637
}
3738
},
3839
{
@@ -44,7 +45,7 @@ internal static partial class OpenApiV2Deserializer
4445
{
4546
"minimum", (o, n) =>
4647
{
47-
o.Minimum = decimal.Parse(n.GetScalarValue());
48+
o.Minimum = decimal.Parse(n.GetScalarValue(), CultureInfo.InvariantCulture);
4849
}
4950
},
5051
{
@@ -56,13 +57,13 @@ internal static partial class OpenApiV2Deserializer
5657
{
5758
"maxLength", (o, n) =>
5859
{
59-
o.MaxLength = int.Parse(n.GetScalarValue());
60+
o.MaxLength = int.Parse(n.GetScalarValue(), CultureInfo.InvariantCulture);
6061
}
6162
},
6263
{
6364
"minLength", (o, n) =>
6465
{
65-
o.MinLength = int.Parse(n.GetScalarValue());
66+
o.MinLength = int.Parse(n.GetScalarValue(), CultureInfo.InvariantCulture);
6667
}
6768
},
6869
{
@@ -74,13 +75,13 @@ internal static partial class OpenApiV2Deserializer
7475
{
7576
"maxItems", (o, n) =>
7677
{
77-
o.MaxItems = int.Parse(n.GetScalarValue());
78+
o.MaxItems = int.Parse(n.GetScalarValue(), CultureInfo.InvariantCulture);
7879
}
7980
},
8081
{
8182
"minItems", (o, n) =>
8283
{
83-
o.MinItems = int.Parse(n.GetScalarValue());
84+
o.MinItems = int.Parse(n.GetScalarValue(), CultureInfo.InvariantCulture);
8485
}
8586
},
8687
{
@@ -92,13 +93,13 @@ internal static partial class OpenApiV2Deserializer
9293
{
9394
"maxProperties", (o, n) =>
9495
{
95-
o.MaxProperties = int.Parse(n.GetScalarValue());
96+
o.MaxProperties = int.Parse(n.GetScalarValue(), CultureInfo.InvariantCulture);
9697
}
9798
},
9899
{
99100
"minProperties", (o, n) =>
100101
{
101-
o.MinProperties = int.Parse(n.GetScalarValue());
102+
o.MinProperties = int.Parse(n.GetScalarValue(), CultureInfo.InvariantCulture);
102103
}
103104
},
104105
{

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

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
using Microsoft.OpenApi.Models;
77
using Microsoft.OpenApi.Readers.ParseNodes;
88
using System.Collections.Generic;
9+
using System.Globalization;
910

1011
namespace Microsoft.OpenApi.Readers.V3
1112
{
@@ -26,13 +27,13 @@ internal static partial class OpenApiV3Deserializer
2627
{
2728
"multipleOf", (o, n) =>
2829
{
29-
o.MultipleOf = decimal.Parse(n.GetScalarValue());
30+
o.MultipleOf = decimal.Parse(n.GetScalarValue(), CultureInfo.InvariantCulture);
3031
}
3132
},
3233
{
3334
"maximum", (o, n) =>
3435
{
35-
o.Maximum = decimal.Parse(n.GetScalarValue());
36+
o.Maximum = decimal.Parse(n.GetScalarValue(), CultureInfo.InvariantCulture);
3637
}
3738
},
3839
{
@@ -44,7 +45,7 @@ internal static partial class OpenApiV3Deserializer
4445
{
4546
"minimum", (o, n) =>
4647
{
47-
o.Minimum = decimal.Parse(n.GetScalarValue());
48+
o.Minimum = decimal.Parse(n.GetScalarValue(), CultureInfo.InvariantCulture);
4849
}
4950
},
5051
{
@@ -56,13 +57,13 @@ internal static partial class OpenApiV3Deserializer
5657
{
5758
"maxLength", (o, n) =>
5859
{
59-
o.MaxLength = int.Parse(n.GetScalarValue());
60+
o.MaxLength = int.Parse(n.GetScalarValue(), CultureInfo.InvariantCulture);
6061
}
6162
},
6263
{
6364
"minLength", (o, n) =>
6465
{
65-
o.MinLength = int.Parse(n.GetScalarValue());
66+
o.MinLength = int.Parse(n.GetScalarValue(), CultureInfo.InvariantCulture);
6667
}
6768
},
6869
{
@@ -74,13 +75,13 @@ internal static partial class OpenApiV3Deserializer
7475
{
7576
"maxItems", (o, n) =>
7677
{
77-
o.MaxItems = int.Parse(n.GetScalarValue());
78+
o.MaxItems = int.Parse(n.GetScalarValue(), CultureInfo.InvariantCulture);
7879
}
7980
},
8081
{
8182
"minItems", (o, n) =>
8283
{
83-
o.MinItems = int.Parse(n.GetScalarValue());
84+
o.MinItems = int.Parse(n.GetScalarValue(), CultureInfo.InvariantCulture);
8485
}
8586
},
8687
{
@@ -92,13 +93,13 @@ internal static partial class OpenApiV3Deserializer
9293
{
9394
"maxProperties", (o, n) =>
9495
{
95-
o.MaxProperties = int.Parse(n.GetScalarValue());
96+
o.MaxProperties = int.Parse(n.GetScalarValue(), CultureInfo.InvariantCulture);
9697
}
9798
},
9899
{
99100
"minProperties", (o, n) =>
100101
{
101-
o.MinProperties = int.Parse(n.GetScalarValue());
102+
o.MinProperties = int.Parse(n.GetScalarValue(), CultureInfo.InvariantCulture);
102103
}
103104
},
104105
{

test/Microsoft.OpenApi.Readers.Tests/V2Tests/OpenApiDocumentTests.cs

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
// Licensed under the MIT license.
33

44
using System.Collections.Generic;
5+
using System.Globalization;
6+
using System.Threading;
57
using FluentAssertions;
68
using Microsoft.OpenApi.Exceptions;
79
using Microsoft.OpenApi.Models;
@@ -66,5 +68,76 @@ public void ShouldThrowWhenReferenceDoesNotExist()
6668
new OpenApiError( new OpenApiException("Invalid Reference identifier 'doesnotexist'.")) });
6769
doc.Should().NotBeNull();
6870
}
71+
72+
[Theory]
73+
[InlineData("en-US")]
74+
[InlineData("hi-IN")]
75+
// The equivalent of English 1,000.36 in French and Danish is 1.000,36
76+
[InlineData("fr-FR")]
77+
[InlineData("da-DK")]
78+
public void ParseDocumentWithDifferentCultureShouldSucceed(string culture)
79+
{
80+
Thread.CurrentThread.CurrentCulture = new CultureInfo(culture);
81+
Thread.CurrentThread.CurrentUICulture = new CultureInfo(culture);
82+
83+
var openApiDoc = new OpenApiStringReader().Read(
84+
@"
85+
swagger: 2.0
86+
info:
87+
title: Simple Document
88+
version: 0.9.1
89+
definitions:
90+
sampleSchema:
91+
type: object
92+
properties:
93+
sampleProperty:
94+
type: double
95+
minimum: 100.54
96+
maximum: 60,000,000.35
97+
exclusiveMaximum: true
98+
exclusiveMinimum: false
99+
paths: {}",
100+
out var context);
101+
102+
openApiDoc.ShouldBeEquivalentTo(
103+
new OpenApiDocument
104+
{
105+
Info = new OpenApiInfo
106+
{
107+
Title = "Simple Document",
108+
Version = "0.9.1"
109+
},
110+
Components = new OpenApiComponents()
111+
{
112+
Schemas =
113+
{
114+
["sampleSchema"] = new OpenApiSchema()
115+
{
116+
Type = "object",
117+
Properties =
118+
{
119+
["sampleProperty"] = new OpenApiSchema()
120+
{
121+
Type = "double",
122+
Minimum = (decimal)100.54,
123+
Maximum = (decimal)60000000.35,
124+
ExclusiveMaximum = true,
125+
ExclusiveMinimum = false
126+
}
127+
},
128+
Reference = new OpenApiReference()
129+
{
130+
Id = "sampleSchema",
131+
Type = ReferenceType.Schema
132+
}
133+
}
134+
}
135+
},
136+
Paths = new OpenApiPaths()
137+
});
138+
139+
context.ShouldBeEquivalentTo(
140+
new OpenApiDiagnostic() { SpecificationVersion = OpenApiSpecVersion.OpenApi2_0 });
141+
}
69142
}
70143
}

0 commit comments

Comments
 (0)