Fix culture handling in legacy System.Json library (dotnet/corefx#34811)
authorStephen Toub <stoub@microsoft.com>
Fri, 25 Jan 2019 03:43:22 +0000 (22:43 -0500)
committerGitHub <noreply@github.com>
Fri, 25 Jan 2019 03:43:22 +0000 (22:43 -0500)
* Fix System.Json code using NumberFormatInfo.InvariantInfo

The intention here was obviously to use invariant culture, but NumberFormatInfo will be ignored for various primitive types that don't respect NFI, e.g. TimeSpan, which respects DateTimeFormatInfo.

* Fix handling of culture in System.Json tests

In particular, CultureInfo.CurrentCulture should not be modified in-process in tests, as on UWP it bleeds across threads.

Commit migrated from https://github.com/dotnet/corefx/commit/f398b6f7c3d08d8e437939cbd9ef29cb3beda1db

src/libraries/System.Json/src/System/Json/JsonPrimitive.cs
src/libraries/System.Json/src/System/Json/JsonValue.cs
src/libraries/System.Json/tests/JsonPrimitiveTests.cs
src/libraries/System.Json/tests/JsonValueTests.cs
src/libraries/System.Json/tests/System.Json.Tests.csproj

index 754783b..86a74e9 100644 (file)
@@ -159,8 +159,8 @@ namespace System.Json
 
                 case JsonType.Number:
                     string s = _value is float || _value is double ?
-                        ((IFormattable)_value).ToString("R", NumberFormatInfo.InvariantInfo) : // Use "round-trip" format
-                        ((IFormattable)_value).ToString("G", NumberFormatInfo.InvariantInfo);
+                        ((IFormattable)_value).ToString("R", CultureInfo.InvariantCulture) : // Use "round-trip" format
+                        ((IFormattable)_value).ToString("G", CultureInfo.InvariantCulture);
                     return s == "NaN" || s == "Infinity" || s == "-Infinity" ?
                         "\"" + s + "\"" :
                         s;
index 4fdae75..3a6a14c 100644 (file)
@@ -350,7 +350,7 @@ namespace System.Json
                 throw new ArgumentNullException(nameof(value));
             }
 
-            return Convert.ToBoolean(((JsonPrimitive)value).Value, NumberFormatInfo.InvariantInfo);
+            return Convert.ToBoolean(((JsonPrimitive)value).Value, CultureInfo.InvariantCulture);
         }
 
         public static implicit operator byte (JsonValue value)
@@ -360,7 +360,7 @@ namespace System.Json
                 throw new ArgumentNullException(nameof(value));
             }
 
-            return Convert.ToByte(((JsonPrimitive)value).Value, NumberFormatInfo.InvariantInfo);
+            return Convert.ToByte(((JsonPrimitive)value).Value, CultureInfo.InvariantCulture);
         }
 
         public static implicit operator char (JsonValue value)
@@ -370,7 +370,7 @@ namespace System.Json
                 throw new ArgumentNullException(nameof(value));
             }
 
-            return Convert.ToChar(((JsonPrimitive)value).Value, NumberFormatInfo.InvariantInfo);
+            return Convert.ToChar(((JsonPrimitive)value).Value, CultureInfo.InvariantCulture);
         }
 
         public static implicit operator decimal (JsonValue value)
@@ -380,14 +380,14 @@ namespace System.Json
                 throw new ArgumentNullException(nameof(value));
             }
 
-            return Convert.ToDecimal(((JsonPrimitive)value).Value, NumberFormatInfo.InvariantInfo);
+            return Convert.ToDecimal(((JsonPrimitive)value).Value, CultureInfo.InvariantCulture);
         }
 
         public static implicit operator double (JsonValue value)
         {
             if (value == null)
                 throw new ArgumentNullException(nameof(value));
-            return Convert.ToDouble(((JsonPrimitive)value).Value, NumberFormatInfo.InvariantInfo);
+            return Convert.ToDouble(((JsonPrimitive)value).Value, CultureInfo.InvariantCulture);
         }
 
         public static implicit operator float (JsonValue value)
@@ -397,7 +397,7 @@ namespace System.Json
                 throw new ArgumentNullException(nameof(value));
             }
 
-            return Convert.ToSingle(((JsonPrimitive)value).Value, NumberFormatInfo.InvariantInfo);
+            return Convert.ToSingle(((JsonPrimitive)value).Value, CultureInfo.InvariantCulture);
         }
 
         public static implicit operator int (JsonValue value)
@@ -407,7 +407,7 @@ namespace System.Json
                 throw new ArgumentNullException(nameof(value));
             }
 
-            return Convert.ToInt32(((JsonPrimitive)value).Value, NumberFormatInfo.InvariantInfo);
+            return Convert.ToInt32(((JsonPrimitive)value).Value, CultureInfo.InvariantCulture);
         }
 
         public static implicit operator long (JsonValue value)
@@ -417,7 +417,7 @@ namespace System.Json
                 throw new ArgumentNullException(nameof(value));
             }
 
-            return Convert.ToInt64(((JsonPrimitive)value).Value, NumberFormatInfo.InvariantInfo);
+            return Convert.ToInt64(((JsonPrimitive)value).Value, CultureInfo.InvariantCulture);
         }
 
         [CLSCompliant(false)]
@@ -428,7 +428,7 @@ namespace System.Json
                 throw new ArgumentNullException(nameof(value));
             }
 
-            return Convert.ToSByte(((JsonPrimitive)value).Value, NumberFormatInfo.InvariantInfo);
+            return Convert.ToSByte(((JsonPrimitive)value).Value, CultureInfo.InvariantCulture);
         }
 
         public static implicit operator short (JsonValue value)
@@ -438,7 +438,7 @@ namespace System.Json
                 throw new ArgumentNullException(nameof(value));
             }
 
-            return Convert.ToInt16(((JsonPrimitive)value).Value, NumberFormatInfo.InvariantInfo);
+            return Convert.ToInt16(((JsonPrimitive)value).Value, CultureInfo.InvariantCulture);
         }
 
         public static implicit operator string (JsonValue value)
@@ -456,7 +456,7 @@ namespace System.Json
                 throw new ArgumentNullException(nameof(value));
             }
 
-            return Convert.ToUInt32(((JsonPrimitive)value).Value, NumberFormatInfo.InvariantInfo);
+            return Convert.ToUInt32(((JsonPrimitive)value).Value, CultureInfo.InvariantCulture);
         }
 
         [CLSCompliant(false)]
@@ -467,7 +467,7 @@ namespace System.Json
                 throw new ArgumentNullException(nameof(value));
             }
 
-            return Convert.ToUInt64(((JsonPrimitive)value).Value, NumberFormatInfo.InvariantInfo);
+            return Convert.ToUInt64(((JsonPrimitive)value).Value, CultureInfo.InvariantCulture);
         }
 
         [CLSCompliant(false)]
@@ -478,7 +478,7 @@ namespace System.Json
                 throw new ArgumentNullException(nameof(value));
             }
 
-            return Convert.ToUInt16(((JsonPrimitive)value).Value, NumberFormatInfo.InvariantInfo);
+            return Convert.ToUInt16(((JsonPrimitive)value).Value, CultureInfo.InvariantCulture);
         }
 
         public static implicit operator DateTime(JsonValue value)
index c19c41f..c30caa2 100644 (file)
@@ -1,6 +1,7 @@
 // Licensed to the .NET Foundation under one or more agreements.
 // See the LICENSE file in the project root for more information.
 
+using System.Globalization;
 using System.IO;
 using System.Text;
 using Xunit;
@@ -76,13 +77,13 @@ namespace System.Json.Tests
         [Fact]
         public void ToString_DateTimeOffset()
         {
-            ToString(new JsonPrimitive(DateTimeOffset.MinValue), DateTimeOffset.MinValue.ToString("G"));
+            ToString(new JsonPrimitive(DateTimeOffset.MinValue), DateTimeOffset.MinValue.ToString("G", CultureInfo.InvariantCulture));
         }
 
         [Fact]
         public void ToString_TimeSpan()
         {
-            ToString(new JsonPrimitive(TimeSpan.Zero), TimeSpan.Zero.ToString("G"));
+            ToString(new JsonPrimitive(TimeSpan.Zero), TimeSpan.Zero.ToString("G", CultureInfo.InvariantCulture));
         }
         
         private void ToString(JsonValue primitive, string expected)
index 06d61d8..ea64ddc 100644 (file)
@@ -1,6 +1,7 @@
 // Licensed to the .NET Foundation under one or more agreements.
 // See the LICENSE file in the project root for more information.
 
+using System.Diagnostics;
 using System.IO;
 using System.Text;
 using System.Globalization;
@@ -10,7 +11,7 @@ using System.Collections.Generic;
 
 namespace System.Json.Tests
 {
-    public class JsonValueTests
+    public class JsonValueTests : RemoteExecutorTestBase
     {
         [Fact]
         public void JsonValue_Load_LoadWithTrailingCommaInDictionary()
@@ -235,19 +236,14 @@ namespace System.Json.Tests
         [InlineData("0.000000000000000000000000000011", 1.1E-29)]
         public void JsonValue_Parse_Double(string json, double expected)
         {
-            foreach (string culture in new[] { "en", "fr", "de" })
+            RemoteInvoke((jsonInner, expectedInner) =>
             {
-                CultureInfo old = CultureInfo.CurrentCulture;
-                try
+                foreach (string culture in new[] { "en", "fr", "de" })
                 {
                     CultureInfo.CurrentCulture = new CultureInfo(culture);
-                    Assert.Equal(expected, (double)JsonValue.Parse(json));
+                    Assert.Equal(double.Parse(expectedInner, CultureInfo.InvariantCulture), (double)JsonValue.Parse(jsonInner));
                 }
-                finally
-                {
-                    CultureInfo.CurrentCulture = old;
-                }
-            }
+            }, json, expected.ToString("R", CultureInfo.InvariantCulture)).Dispose();
         }
 
         // Convert a number to json and parse the string, then compare the result to the original value
@@ -275,19 +271,15 @@ namespace System.Json.Tests
         [InlineData(1.123456789e-28)] // Values around the smallest positive decimal value
         public void JsonValue_Parse_Double_ViaJsonPrimitive(double number)
         {
-            foreach (string culture in new[] { "en", "fr", "de" })
+            RemoteInvoke(numberText =>
             {
-                CultureInfo old = CultureInfo.CurrentCulture;
-                try
+                double numberInner = double.Parse(numberText, CultureInfo.InvariantCulture);
+                foreach (string culture in new[] { "en", "fr", "de" })
                 {
                     CultureInfo.CurrentCulture = new CultureInfo(culture);
-                    Assert.Equal(number, (double)JsonValue.Parse(new JsonPrimitive(number).ToString()));
+                    Assert.Equal(numberInner, (double)JsonValue.Parse(new JsonPrimitive(numberInner).ToString()));
                 }
-                finally
-                {
-                    CultureInfo.CurrentCulture = old;
-                }
-            }
+            }, number.ToString("R", CultureInfo.InvariantCulture)).Dispose();
         }
 
         [Fact]
index 0a35ca1..8ae2a93 100644 (file)
@@ -9,5 +9,9 @@
     <Compile Include="JsonObjectTests.cs" />
     <Compile Include="JsonPrimitiveTests.cs" />
     <Compile Include="JsonValueTests.cs" />
+    <ProjectReference Include="$(CommonTestPath)\System\Diagnostics\RemoteExecutorConsoleApp\RemoteExecutorConsoleApp.csproj">
+      <Project>{69e46a6f-9966-45a5-8945-2559fe337827}</Project>
+      <Name>RemoteExecutorConsoleApp</Name>
+    </ProjectReference>
   </ItemGroup>
 </Project>
\ No newline at end of file