Skip to content

Commit fd864e2

Browse files
authored
Merge pull request graphql-dotnet#434 from graphql-dotnet/bugs/doubles
Another attempt at fixing the decimal number parsing/printing
2 parents bc3bb87 + 6bf00db commit fd864e2

14 files changed

+247
-28
lines changed
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Globalization;
4+
5+
namespace GraphQL.Tests
6+
{
7+
public class CultureTestHelper
8+
{
9+
public static IEnumerable<CultureInfo> Cultures => new[]
10+
{
11+
new CultureInfo("fi-FI"),
12+
new CultureInfo("en-US"),
13+
CultureInfo.InvariantCulture,
14+
new CultureInfo(CultureInfo.CurrentUICulture.Name),
15+
new CultureInfo(CultureInfo.CurrentCulture.Name)
16+
};
17+
18+
public static void UseCultures(Action scope)
19+
{
20+
foreach (var culture in Cultures)
21+
{
22+
UseCulture(culture, scope);
23+
}
24+
}
25+
26+
public static void UseCulture(CultureInfo culture, Action scope)
27+
{
28+
var before = CultureInfo.CurrentCulture;
29+
var beforeUi = CultureInfo.CurrentUICulture;
30+
CultureInfo.DefaultThreadCurrentCulture = culture;
31+
CultureInfo.DefaultThreadCurrentUICulture = culture;
32+
33+
try
34+
{
35+
scope();
36+
}
37+
finally
38+
{
39+
CultureInfo.DefaultThreadCurrentCulture = before;
40+
CultureInfo.DefaultThreadCurrentUICulture = beforeUi;
41+
}
42+
}
43+
}
44+
}
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Globalization;
4+
using System.Text;
5+
using GraphQL.Types;
6+
using Shouldly;
7+
using Xunit;
8+
9+
namespace GraphQL.Tests
10+
{
11+
public class ObjectExtensionsTests
12+
{
13+
[Fact]
14+
public void convert_double_using_cultures()
15+
{
16+
CultureTestHelper.UseCultures(convert_double);
17+
}
18+
19+
[Fact]
20+
public void convert_double()
21+
{
22+
/* Given */
23+
double value = 123.123d;
24+
Type floatType = typeof(double);
25+
26+
27+
/* When */
28+
var actual = ObjectExtensions.ConvertValue(value, floatType);
29+
30+
/* Then */
31+
actual.ShouldBe(value);
32+
}
33+
34+
[Fact]
35+
public void convert_decimal_using_cultures()
36+
{
37+
CultureTestHelper.UseCultures(convert_decimal);
38+
}
39+
40+
[Fact]
41+
public void convert_decimal()
42+
{
43+
/* Given */
44+
decimal value = 123.123m;
45+
Type floatType = typeof(decimal);
46+
47+
48+
/* When */
49+
var actual = ObjectExtensions.ConvertValue(value, floatType);
50+
51+
/* Then */
52+
actual.ShouldBe(value);
53+
}
54+
55+
[Fact]
56+
public void convert_single_using_cultures()
57+
{
58+
CultureTestHelper.UseCultures(convert_single);
59+
}
60+
61+
[Fact]
62+
public void convert_single()
63+
{
64+
65+
/* Given */
66+
float value = 123.123f;
67+
Type floatType = typeof(float);
68+
69+
70+
/* When */
71+
var actual = ObjectExtensions.ConvertValue(value, floatType);
72+
73+
/* Then */
74+
actual.ShouldBe(value);
75+
}
76+
}
77+
}

src/GraphQL.Tests/Types/DecimalGraphTypeTests.cs

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
using GraphQL.Language.AST;
1+
using System.Globalization;
2+
using GraphQL.Language.AST;
23
using GraphQL.Types;
34
using Shouldly;
45
using Xunit;
@@ -18,7 +19,7 @@ public void coerces_null_to_null()
1819
[Fact]
1920
public void coerces_integer_to_decimal()
2021
{
21-
type.ParseValue(0).ShouldBe((decimal)0);
22+
type.ParseValue(0).ShouldBe((decimal) 0);
2223
}
2324

2425
[Fact]
@@ -27,12 +28,24 @@ public void coerces_invalid_string_to_null()
2728
type.ParseValue("abcd").ShouldBe(null);
2829
}
2930

31+
[Fact]
32+
public void coerces_numeric_string_to_decimal_using_cultures()
33+
{
34+
CultureTestHelper.UseCultures(coerces_numeric_string_to_decimal);
35+
}
36+
3037
[Fact]
3138
public void coerces_numeric_string_to_decimal()
3239
{
3340
type.ParseValue("12345.6579").ShouldBe((decimal)12345.6579);
3441
}
3542

43+
[Fact]
44+
public void converts_DecimalValue_to_decimal_using_cultures()
45+
{
46+
CultureTestHelper.UseCultures(converts_DecimalValue_to_decimal);
47+
}
48+
3649
[Fact]
3750
public void converts_DecimalValue_to_decimal()
3851
{

src/GraphQL.Tests/Types/FloatGraphTypeTests.cs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
using GraphQL.Types;
1+
using System.Globalization;
2+
using GraphQL.Types;
23
using Shouldly;
34
using Xunit;
45

@@ -20,6 +21,12 @@ public void coerces_invalid_string_to_null()
2021
type.ParseValue("abcd").ShouldBe(null);
2122
}
2223

24+
[Fact]
25+
public void coerces_double_to_value_using_cultures()
26+
{
27+
CultureTestHelper.UseCultures(coerces_double_to_value);
28+
}
29+
2330
[Fact]
2431
public void coerces_double_to_value()
2532
{

src/GraphQL.Tests/Utilities/AstPrinterTests.cs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
using GraphQL.Execution;
1+
using System.Globalization;
2+
using GraphQL.Execution;
23
using GraphQL.Language.AST;
34
using GraphQL.Utilities;
45
using Shouldly;
@@ -64,14 +65,20 @@ public void prints_long_value()
6465
result.ShouldBe(value);
6566
}
6667

68+
[Fact]
69+
public void prints_float_value_using_cultures()
70+
{
71+
CultureTestHelper.UseCultures(prints_float_value);
72+
}
73+
6774
[Fact]
6875
public void prints_float_value()
6976
{
7077
double value = 3.33;
7178

7279
var val = new FloatValue(value);
7380
var result = _printer.Visit(val);
74-
result.ShouldBe($"{value, 0:0.0##}");
81+
result.ShouldBe(value.ToString("0.0##", NumberFormatInfo.InvariantInfo));
7582
}
7683

7784
private static string MonetizeLineBreaks(string input)

src/GraphQL.Tests/Validation/ArgumentsOfCorrectTypeTests.cs

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
using System.Collections.Generic;
1+
using System.Collections.Generic;
2+
using System.Globalization;
23
using GraphQL.Validation.Rules;
34
using Xunit;
45

@@ -122,6 +123,12 @@ public void int_into_string()
122123
});
123124
}
124125

126+
[Fact]
127+
public void float_into_string_using_cultures()
128+
{
129+
CultureTestHelper.UseCultures(float_into_string);
130+
}
131+
125132
[Fact]
126133
public void float_into_string()
127134
{
@@ -426,6 +433,12 @@ public void int_into_enum()
426433
});
427434
}
428435

436+
[Fact]
437+
public void float_into_enum_with_cultures()
438+
{
439+
CultureTestHelper.UseCultures(float_into_enum);
440+
}
441+
429442
[Fact]
430443
public void float_into_enum()
431444
{

src/GraphQL/Conversion/Conversions.cs

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1-
using System;
1+
using System;
22
using System.Collections.Generic;
3+
using System.Globalization;
34
using System.Linq;
45

56
namespace GraphQL.Conversion
@@ -28,9 +29,9 @@ public Conversions()
2829
char.TryParse(x, out c);
2930
return c;
3031
});
31-
RegisterConversion(decimal.Parse);
32-
RegisterConversion(double.Parse);
33-
RegisterConversion(float.Parse);
32+
RegisterConversion(ParseDecimal);
33+
RegisterConversion(ParseDouble);
34+
RegisterConversion(ParseFloat);
3435
RegisterConversion(short.Parse);
3536
RegisterConversion(int.Parse);
3637
RegisterConversion(long.Parse);
@@ -48,6 +49,21 @@ public Conversions()
4849
});
4950
}
5051

52+
public static float ParseFloat(string value)
53+
{
54+
return System.Convert.ToSingle(value, NumberFormatInfo.InvariantInfo);
55+
}
56+
57+
public static double ParseDouble(string value)
58+
{
59+
return System.Convert.ToDouble(value, NumberFormatInfo.InvariantInfo);
60+
}
61+
62+
public static decimal ParseDecimal(string value)
63+
{
64+
return System.Convert.ToDecimal(value, NumberFormatInfo.InvariantInfo);
65+
}
66+
5167
private IEnumerable<IConversionProvider> providers()
5268
{
5369
foreach (var provider in _providers)

src/GraphQL/Language/AST/IntValue.cs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1-
using System;
1+
using System;
22
using System.Collections.Generic;
3+
using System.Globalization;
34
using System.Linq;
45

56
namespace GraphQL.Language.AST
@@ -99,7 +100,8 @@ public FloatValue(double value)
99100

100101
public override string ToString()
101102
{
102-
return "FloatValue{{value={0}}}".ToFormat(Value);
103+
var valueStr = Value.ToString("G", NumberFormatInfo.InvariantInfo);
104+
return "FloatValue{{value={0}}}".ToFormat(valueStr);
103105
}
104106

105107
protected bool Equals(FloatValue other)

src/GraphQL/Language/CoreToVanillaConverter.cs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1-
using System.Collections.Generic;
1+
using System.Collections.Generic;
22
using System.Linq;
3+
using GraphQL.Conversion;
34
using GraphQL.Language.AST;
45
using GraphQLParser;
56
using GraphQLParser.AST;
@@ -202,7 +203,8 @@ public IValue Value(GraphQLValue source)
202203
case ASTNodeKind.FloatValue:
203204
{
204205
var str = source as GraphQLScalarValue;
205-
return new FloatValue(double.Parse(str.Value)).WithLocation(str, _body);
206+
var value = Conversions.ParseDouble(str.Value);
207+
return new FloatValue(value).WithLocation(str, _body);
206208
}
207209
case ASTNodeKind.BooleanValue:
208210
{

src/GraphQL/ObjectExtensions.cs

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
1-
using System;
1+
using System;
22
using System.Collections;
33
using System.Collections.Generic;
4+
using System.Globalization;
45
using System.Linq;
56
using System.Reflection;
67
using GraphQL.Conversion;
@@ -135,7 +136,16 @@ public static object ConvertValue(object value, Type fieldType)
135136
return value;
136137
}
137138

138-
var text = value.ToString();
139+
string text;
140+
if (value is float)
141+
text = ((float)value).ToString(CultureInfo.InvariantCulture);
142+
else if (value is double)
143+
text = ((double)value).ToString(CultureInfo.InvariantCulture);
144+
else if (value is decimal)
145+
text = ((decimal)value).ToString(CultureInfo.InvariantCulture);
146+
else
147+
text = value.ToString();
148+
139149
return _conversions.Value.Convert(fieldType, text);
140150
}
141151

src/GraphQL/Types/DecimalGraphType.cs

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
using System;
2+
using System.Globalization;
3+
using GraphQL.Conversion;
14
using GraphQL.Language.AST;
25

36
namespace GraphQL.Types
@@ -16,12 +19,20 @@ public override object Serialize(object value)
1619

1720
public override object ParseValue(object value)
1821
{
19-
decimal result;
20-
if (decimal.TryParse(value?.ToString() ?? string.Empty, out result))
22+
if (value == null)
23+
return null;
24+
25+
try
2126
{
27+
var result = Convert.ToDecimal(value, NumberFormatInfo.InvariantInfo);
28+
2229
return result;
2330
}
24-
return null;
31+
catch (FormatException e)
32+
{
33+
//todo: should log or something?
34+
return null;
35+
}
2536
}
2637

2738
public override object ParseLiteral(IValue value)

0 commit comments

Comments
 (0)