/// <seealso cref="TokenType" />
/// </exception>
/// <exception cref="FormatException">
- /// Thrown if the JSON token value represents a number less than <see cref="float.MinValue"/> or greater
+ /// On any framework that is not .NET Core 3.0 or higher, thrown if the JSON token value represents a number less than <see cref="float.MinValue"/> or greater
/// than <see cref="float.MaxValue"/>.
/// </exception>
public float GetSingle()
/// <seealso cref="TokenType" />
/// </exception>
/// <exception cref="FormatException">
- /// Thrown if the JSON token value represents a number less than <see cref="double.MinValue"/> or greater
+ /// On any framework that is not .NET Core 3.0 or higher, thrown if the JSON token value represents a number less than <see cref="double.MinValue"/> or greater
/// than <see cref="double.MaxValue"/>.
/// </exception>
public double GetDouble()
using System.Buffers;
using System.Buffers.Text;
using System.Diagnostics;
+using System.Globalization;
namespace System.Text.Json
{
/// Thrown if this would result in an invalid JSON to be written (while validation is enabled).
/// </exception>
/// <remarks>
- /// Writes the <see cref="double"/> using the default <see cref="StandardFormat"/> (i.e. 'G').
+ /// Writes the <see cref="double"/> using the default <see cref="StandardFormat"/> on .NET Core 3 or higher
+ /// and 'G17' on any other framework.
/// </remarks>
public void WriteNumberValue(double value)
{
BytesPending += indent;
}
- bool result = Utf8Formatter.TryFormat(value, output.Slice(BytesPending), out int bytesWritten);
+ bool result = TryFormatDouble(value, output.Slice(BytesPending), out int bytesWritten);
Debug.Assert(result);
BytesPending += bytesWritten;
}
+
+ private static bool TryFormatDouble(double value, Span<byte> destination, out int bytesWritten)
+ {
+ // Frameworks that are not .NET Core 3.0 or higher do not produce roundtrippable strings by
+ // default. Further, the Utf8Formatter on older frameworks does not support taking a precision
+ // specifier for 'G' nor does it represent other formats such as 'R'. As such, we duplicate
+ // the .NET Core 3.0 logic of forwarding to the UTF16 formatter and transcoding it back to UTF8,
+ // with some additional changes to remove dependencies on Span APIs which don't exist downlevel.
+
+#if BUILDING_INBOX_LIBRARY
+ return Utf8Formatter.TryFormat(value, destination, out bytesWritten);
+#else
+ const string FormatString = "G17";
+
+ string utf16Text = value.ToString(FormatString, CultureInfo.InvariantCulture);
+
+ // Copy the value to the destination, if it's large enough.
+
+ if (utf16Text.Length > destination.Length)
+ {
+ bytesWritten = 0;
+ return false;
+ }
+
+ try
+ {
+ byte[] bytes = Encoding.UTF8.GetBytes(utf16Text);
+
+ if (bytes.Length > destination.Length)
+ {
+ bytesWritten = 0;
+ return false;
+ }
+
+ bytes.CopyTo(destination);
+ bytesWritten = bytes.Length;
+
+ return true;
+ }
+ catch
+ {
+ bytesWritten = 0;
+ return false;
+ }
+#endif
+ }
}
}
using System.Buffers;
using System.Buffers.Text;
using System.Diagnostics;
+using System.Globalization;
namespace System.Text.Json
{
/// Thrown if this would result in an invalid JSON to be written (while validation is enabled).
/// </exception>
/// <remarks>
- /// Writes the <see cref="float"/> using the default <see cref="StandardFormat"/> (i.e. 'G').
+ /// Writes the <see cref="float"/> using the default <see cref="StandardFormat"/> on .NET Core 3 or higher
+ /// and 'G9' on any other framework.
/// </remarks>
public void WriteNumberValue(float value)
{
BytesPending += indent;
}
- bool result = Utf8Formatter.TryFormat(value, output.Slice(BytesPending), out int bytesWritten);
+ bool result = TryFormatSingle(value, output.Slice(BytesPending), out int bytesWritten);
Debug.Assert(result);
BytesPending += bytesWritten;
}
+
+ private static bool TryFormatSingle(float value, Span<byte> destination, out int bytesWritten)
+ {
+ // Frameworks that are not .NET Core 3.0 or higher do not produce roundtrippable strings by
+ // default. Further, the Utf8Formatter on older frameworks does not support taking a precision
+ // specifier for 'G' nor does it represent other formats such as 'R'. As such, we duplicate
+ // the .NET Core 3.0 logic of forwarding to the UTF16 formatter and transcoding it back to UTF8,
+ // with some additional changes to remove dependencies on Span APIs which don't exist downlevel.
+
+#if BUILDING_INBOX_LIBRARY
+ return Utf8Formatter.TryFormat(value, destination, out bytesWritten);
+#else
+ const string FormatString = "G9";
+
+ string utf16Text = value.ToString(FormatString, CultureInfo.InvariantCulture);
+
+ // Copy the value to the destination, if it's large enough.
+
+ if (utf16Text.Length > destination.Length)
+ {
+ bytesWritten = 0;
+ return false;
+ }
+
+ try
+ {
+ byte[] bytes = Encoding.UTF8.GetBytes(utf16Text);
+
+ if (bytes.Length > destination.Length)
+ {
+ bytesWritten = 0;
+ return false;
+ }
+
+ bytes.CopyTo(destination);
+ bytesWritten = bytes.Length;
+
+ return true;
+ }
+ catch
+ {
+ bytesWritten = 0;
+ return false;
+ }
+#endif
+ }
}
}
[Fact]
public static void ReadTooPreciseDouble()
{
- // If https://github.com/dotnet/corefx/issues/33997 gets resolved as the reader throwing,
- // this test would need to expect FormatException from GetDouble, and false from TryGet.
using (JsonDocument doc = JsonDocument.Parse(" 1e+100000002"))
{
JsonElement root = doc.RootElement;
[Fact]
public static void ReadArrayWithComments()
{
- // If https://github.com/dotnet/corefx/issues/33997 gets resolved as the reader throwing,
- // this test would need to expect FormatException from GetDouble, and false from TryGet.
var options = new JsonDocumentOptions
{
CommentHandling = JsonCommentHandling.Skip,
// confirm the printing precision of double, like
//
//double precisePi = double.Parse(PrecisePi);
- //Assert.NotEqual(PrecisePi, precisePi.ToString("R"));
+ //Assert.NotEqual(PrecisePi, precisePi.ToString(JsonTestHelper.DoubleFormatString));
WriteSimpleValue(indented, PrecisePi);
}
// https://tools.ietf.org/html/rfc7159#section-6
const string OneQuarticGoogol = "1e400";
- // To confirm that this test is doing what it intends, one could
- // confirm the printing precision of double, like
- //
- //double oneQuarticGoogol = double.Parse(OneQuarticGoogol);
- //Assert.NotEqual(OneQuarticGoogol, oneQuarticGoogol.ToString("R"));
-
+ // This just validates we write the literal number 1e400 even though it is too
+ // large to be represented by System.Double and would be converted to
+ // PositiveInfinity instead (or throw if using double.Parse on frameworks
+ // older than .NET Core 3.0).
WriteSimpleValue(indented, OneQuarticGoogol);
}
0.000,
1.1234e1,
-1.1234e1,
- 1.79769313486231E+308, // double.MaxValue doesn't round trip
- -1.79769313486231E+308 // double.MinValue doesn't round trip
+ double.MaxValue,
+ double.MinValue
};
for (int i = 0; i < numberOfItems / 2; i++)
{
{
// Use InvariantCulture to format the numbers to make sure they retain the decimal point '.'
builder.Append("\"double").Append(i).Append("\": ");
- var str = string.Format(CultureInfo.InvariantCulture, "{0}, ", Doubles[i]);
+ const string Format = "{0:" + JsonTestHelper.DoubleFormatString + "}, ";
+ string str = string.Format(CultureInfo.InvariantCulture, Format, Doubles[i]);
builder.AppendFormat(CultureInfo.InvariantCulture, "{0}", str);
}
for (int i = 0; i < Floats.Count; i++)
{
+ // Use InvariantCulture to format the numbers to make sure they retain the decimal point '.'
builder.Append("\"float").Append(i).Append("\": ");
- var str = string.Format(CultureInfo.InvariantCulture, "{0}, ", Floats[i]);
+ const string Format = "{0:" + JsonTestHelper.SingleFormatString + "}, ";
+ string str = string.Format(CultureInfo.InvariantCulture, Format, Floats[i]);
builder.AppendFormat(CultureInfo.InvariantCulture, "{0}", str);
}
for (int i = 0; i < Decimals.Count; i++)
{
builder.Append("\"decimal").Append(i).Append("\": ");
- var str = string.Format(CultureInfo.InvariantCulture, "{0}, ", Decimals[i]);
+ string str = string.Format(CultureInfo.InvariantCulture, "{0}, ", Decimals[i]);
builder.AppendFormat(CultureInfo.InvariantCulture, "{0}", str);
}
string jsonString = builder.ToString();
JsonData = Encoding.UTF8.GetBytes(jsonString);
}
-
}
}
{
internal static class JsonTestHelper
{
+#if BUILDING_INBOX_LIBRARY
+ public const string DoubleFormatString = null;
+ public const string SingleFormatString = null;
+#else
+ public const string DoubleFormatString = "G17";
+ public const string SingleFormatString = "G9";
+#endif
+
public static string NewtonsoftReturnStringHelper(TextReader reader)
{
var sb = new StringBuilder();
// See the LICENSE file in the project root for more information.
using System.Globalization;
+using System.Runtime.CompilerServices;
using Xunit;
namespace System.Text.Json.Serialization.Tests
}
[Fact]
- [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework, "Skipped since NETFX has different semantics and bugs with floating point.")]
public static void RangePassFloatingPoint()
{
// Verify overflow\underflow.
- // On NETFX these throw.
- Assert.True(float.IsNegativeInfinity(JsonSerializer.Deserialize<float>(double.MinValue.ToString(CultureInfo.InvariantCulture))));
- Assert.True(float.IsPositiveInfinity(JsonSerializer.Deserialize<float>(double.MaxValue.ToString(CultureInfo.InvariantCulture))));
- Assert.True(float.IsNegativeInfinity(JsonSerializer.Deserialize<float?>(double.MinValue.ToString(CultureInfo.InvariantCulture)).Value));
- Assert.True(float.IsPositiveInfinity(JsonSerializer.Deserialize<float?>(double.MaxValue.ToString(CultureInfo.InvariantCulture)).Value));
+ AssertFloatingPointBehavior(netcoreExpectedValue: float.NegativeInfinity, () => JsonSerializer.Deserialize<float>(float.MinValue.ToString(CultureInfo.InvariantCulture) + "0"));
+ AssertFloatingPointBehavior(netcoreExpectedValue: float.PositiveInfinity, () => JsonSerializer.Deserialize<float>(float.MaxValue.ToString(CultureInfo.InvariantCulture) + "0"));
+ AssertFloatingPointBehavior(netcoreExpectedValue: float.NegativeInfinity, () => JsonSerializer.Deserialize<float?>(float.MinValue.ToString(CultureInfo.InvariantCulture) + "0").Value);
+ AssertFloatingPointBehavior(netcoreExpectedValue: float.PositiveInfinity, () => JsonSerializer.Deserialize<float?>(float.MaxValue.ToString(CultureInfo.InvariantCulture) + "0").Value);
- Assert.True(double.IsNegativeInfinity(JsonSerializer.Deserialize<double>(double.MinValue.ToString(CultureInfo.InvariantCulture) + "0")));
- Assert.True(double.IsPositiveInfinity(JsonSerializer.Deserialize<double>(double.MaxValue.ToString(CultureInfo.InvariantCulture) + "0")));
- Assert.True(double.IsNegativeInfinity(JsonSerializer.Deserialize<double?>(double.MinValue.ToString(CultureInfo.InvariantCulture) + "0").Value));
- Assert.True(double.IsPositiveInfinity(JsonSerializer.Deserialize<double?>(double.MaxValue.ToString(CultureInfo.InvariantCulture) + "0").Value));
+ AssertFloatingPointBehavior(netcoreExpectedValue: double.NegativeInfinity, () => JsonSerializer.Deserialize<double>(double.MinValue.ToString(CultureInfo.InvariantCulture) + "0"));
+ AssertFloatingPointBehavior(netcoreExpectedValue: double.PositiveInfinity, () => JsonSerializer.Deserialize<double>(double.MaxValue.ToString(CultureInfo.InvariantCulture) + "0"));
+ AssertFloatingPointBehavior(netcoreExpectedValue: double.NegativeInfinity, () => JsonSerializer.Deserialize<double?>(double.MinValue.ToString(CultureInfo.InvariantCulture) + "0").Value);
+ AssertFloatingPointBehavior(netcoreExpectedValue: double.PositiveInfinity, () => JsonSerializer.Deserialize<double?>(double.MaxValue.ToString(CultureInfo.InvariantCulture) + "0").Value);
// Verify sign is correct.
- // On NETFX a value of -0 does not keep the sign.
- Assert.Equal(0x0000000000000000ul, (ulong)BitConverter.DoubleToInt64Bits(JsonSerializer.Deserialize<double>("0")));
- Assert.Equal(0x8000000000000000ul, (ulong)BitConverter.DoubleToInt64Bits(JsonSerializer.Deserialize<double>("-0")));
- Assert.Equal(0x8000000000000000ul, (ulong)BitConverter.DoubleToInt64Bits(JsonSerializer.Deserialize<double>("-0.0")));
+ AssertFloatingPointBehavior(netfxExpectedValue: 0x00000000u, netcoreExpectedValue: 0x00000000u, () => (uint)SingleToInt32Bits(JsonSerializer.Deserialize<float>("0")));
+ AssertFloatingPointBehavior(netfxExpectedValue: 0x00000000u, netcoreExpectedValue: 0x80000000u, () => (uint)SingleToInt32Bits(JsonSerializer.Deserialize<float>("-0")));
+ AssertFloatingPointBehavior(netfxExpectedValue: 0x00000000u, netcoreExpectedValue: 0x80000000u, () => (uint)SingleToInt32Bits(JsonSerializer.Deserialize<float>("-0.0")));
-#if BUILDING_INBOX_LIBRARY
- // Verify sign is correct; SingleToInt32Bits not available on netfx.
- Assert.Equal(0x00000000u, (uint)BitConverter.SingleToInt32Bits(JsonSerializer.Deserialize<float>("0")));
- Assert.Equal(0x80000000u, (uint)BitConverter.SingleToInt32Bits(JsonSerializer.Deserialize<float>("-0")));
- Assert.Equal(0x80000000u, (uint)BitConverter.SingleToInt32Bits(JsonSerializer.Deserialize<float>("-0.0")));
-#endif
+ AssertFloatingPointBehavior(netfxExpectedValue: 0x0000000000000000ul, netcoreExpectedValue: 0x0000000000000000ul, () => (ulong)BitConverter.DoubleToInt64Bits(JsonSerializer.Deserialize<double>("0")));
+ AssertFloatingPointBehavior(netfxExpectedValue: 0x0000000000000000ul, netcoreExpectedValue: 0x8000000000000000ul, () => (ulong)BitConverter.DoubleToInt64Bits(JsonSerializer.Deserialize<double>("-0")));
+ AssertFloatingPointBehavior(netfxExpectedValue: 0x0000000000000000ul, netcoreExpectedValue: 0x8000000000000000ul, () => (ulong)BitConverter.DoubleToInt64Bits(JsonSerializer.Deserialize<double>("-0.0")));
// Verify Round-tripping.
- // On NETFX round tripping is not supported.
- Assert.Equal(float.MaxValue, JsonSerializer.Deserialize<float>(float.MaxValue.ToString(CultureInfo.InvariantCulture)));
- Assert.Equal(float.MaxValue, JsonSerializer.Deserialize<float?>(float.MaxValue.ToString(CultureInfo.InvariantCulture)));
+ Assert.Equal(float.MaxValue, JsonSerializer.Deserialize<float>(float.MaxValue.ToString(JsonTestHelper.SingleFormatString, CultureInfo.InvariantCulture)));
+ Assert.Equal(float.MaxValue, JsonSerializer.Deserialize<float?>(float.MaxValue.ToString(JsonTestHelper.SingleFormatString, CultureInfo.InvariantCulture)));
- Assert.Equal(double.MaxValue, JsonSerializer.Deserialize<double>(double.MaxValue.ToString(CultureInfo.InvariantCulture)));
- Assert.Equal(double.MaxValue, JsonSerializer.Deserialize<double?>(double.MaxValue.ToString(CultureInfo.InvariantCulture)));
+ Assert.Equal(double.MaxValue, JsonSerializer.Deserialize<double>(double.MaxValue.ToString(JsonTestHelper.DoubleFormatString, CultureInfo.InvariantCulture)));
+ Assert.Equal(double.MaxValue, JsonSerializer.Deserialize<double?>(double.MaxValue.ToString(JsonTestHelper.DoubleFormatString, CultureInfo.InvariantCulture)));
}
[Fact]
uri = JsonSerializer.Deserialize<Uri>(@"""~/path""");
Assert.Equal("~/path", uri.ToString());
}
- }
+
+ private static int SingleToInt32Bits(float value)
+ {
+#if BUILDING_INBOX_LIBRARY
+ return BitConverter.SingleToInt32Bits(value);
+#else
+ return Unsafe.As<float, int>(ref value);
+#endif
+ }
+
+ private static void AssertFloatingPointBehavior<T>(T netcoreExpectedValue, Func<T> testCode)
+ {
+ if (PlatformDetection.IsFullFramework)
+ {
+ Assert.Throws<JsonException>(() => testCode());
+ }
+ else
+ {
+ Assert.Equal(netcoreExpectedValue, testCode());
+ }
+ }
+
+ private static void AssertFloatingPointBehavior<T>(T netfxExpectedValue, T netcoreExpectedValue, Func<T> testCode)
+ {
+ if (PlatformDetection.IsFullFramework)
+ {
+ Assert.Equal(netfxExpectedValue, testCode());
+ }
+ else
+ {
+ Assert.Equal(netcoreExpectedValue, testCode());
+ }
+ }
+ }
}
if (count >= floats.Count)
count = 0;
- var str = string.Format(CultureInfo.InvariantCulture, "{0}", floats[count]);
- float expected = float.Parse(str, CultureInfo.InvariantCulture);
+ string roundTripActual = numberFloat.ToString(JsonTestHelper.SingleFormatString, CultureInfo.InvariantCulture);
+ float actual = float.Parse(roundTripActual, CultureInfo.InvariantCulture);
- Assert.Equal(expected, numberFloat);
+ string roundTripExpected = floats[count].ToString(JsonTestHelper.SingleFormatString, CultureInfo.InvariantCulture);
+ float expected = float.Parse(roundTripExpected, CultureInfo.InvariantCulture);
+
+ Assert.Equal(expected, actual);
count++;
}
else if (key.StartsWith("double"))
if (count >= doubles.Count)
count = 0;
- string roundTripActual = numberDouble.ToString("R", CultureInfo.InvariantCulture);
+ string roundTripActual = numberDouble.ToString(JsonTestHelper.DoubleFormatString, CultureInfo.InvariantCulture);
double actual = double.Parse(roundTripActual, CultureInfo.InvariantCulture);
- string roundTripExpected = doubles[count].ToString("R", CultureInfo.InvariantCulture);
+ string roundTripExpected = doubles[count].ToString(JsonTestHelper.DoubleFormatString, CultureInfo.InvariantCulture);
double expected = double.Parse(roundTripExpected, CultureInfo.InvariantCulture);
- // Temporary work around for precision/round-tripping issues with Utf8Parser
- // https://github.com/dotnet/corefx/issues/33360
- if (expected != actual)
- {
- double diff = Math.Abs(expected - actual);
- Assert.True(diff < 1E-9 || diff > 1E288);
- }
- else
- {
- Assert.Equal(expected, actual);
- }
+ Assert.Equal(expected, actual);
count++;
}
else if (key.StartsWith("decimal"))
if (count >= floats.Count)
count = 0;
- var str = string.Format(CultureInfo.InvariantCulture, "{0}", floats[count]);
- float expected = float.Parse(str, CultureInfo.InvariantCulture);
+ string roundTripActual = json.GetSingle().ToString(JsonTestHelper.SingleFormatString, CultureInfo.InvariantCulture);
+ float actual = float.Parse(roundTripActual, CultureInfo.InvariantCulture);
- Assert.Equal(expected, json.GetSingle());
+ string roundTripExpected = floats[count].ToString(JsonTestHelper.SingleFormatString, CultureInfo.InvariantCulture);
+ float expected = float.Parse(roundTripExpected, CultureInfo.InvariantCulture);
+
+ Assert.Equal(expected, actual);
count++;
}
else if (key.StartsWith("double"))
if (count >= doubles.Count)
count = 0;
- string roundTripActual = json.GetDouble().ToString("R", CultureInfo.InvariantCulture);
+ string roundTripActual = json.GetDouble().ToString(JsonTestHelper.DoubleFormatString, CultureInfo.InvariantCulture);
double actual = double.Parse(roundTripActual, CultureInfo.InvariantCulture);
- string roundTripExpected = doubles[count].ToString("R", CultureInfo.InvariantCulture);
+ string roundTripExpected = doubles[count].ToString(JsonTestHelper.DoubleFormatString, CultureInfo.InvariantCulture);
double expected = double.Parse(roundTripExpected, CultureInfo.InvariantCulture);
- // Temporary work around for precision/round-tripping issues with Utf8Parser
- // https://github.com/dotnet/corefx/issues/33360
- if (expected != actual)
- {
- double diff = Math.Abs(expected - actual);
- Assert.True(diff < 1E-9 || diff > 1E288);
- }
- else
- {
- Assert.Equal(expected, actual);
- }
+ Assert.Equal(expected, actual);
count++;
}
else if (key.StartsWith("decimal"))
[Theory]
[InlineData("-4.402823E+38", float.NegativeInfinity, -4.402823E+38)] // float.MinValue - 1
[InlineData("4.402823E+38", float.PositiveInfinity, 4.402823E+38)] // float.MaxValue + 1
- [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework, "Utf8Parser does not support parsing really large float and double values to infinity.")]
public static void TestingTooLargeSingleConversionToInfinity(string jsonString, float expectedFloat, double expectedDouble)
{
byte[] dataUtf8 = Encoding.UTF8.GetBytes(jsonString);
{
if (json.TokenType == JsonTokenType.Number)
{
- Assert.True(json.TryGetSingle(out float floatValue));
- Assert.Equal(expectedFloat, floatValue);
+ if (PlatformDetection.IsFullFramework)
+ {
+ // Full framework throws for overflow rather than returning Infinity
+ // This was fixed for .NET Core 3.0 in order to be IEEE 754 compliant
+
+ Assert.False(json.TryGetSingle(out float _));
+
+ try
+ {
+ json.GetSingle();
+ Assert.True(false, $"Expected {nameof(FormatException)}.");
+ }
+ catch (FormatException)
+ {
+ /* Expected exception */
+ }
+ }
+ else
+ {
+ Assert.True(json.TryGetSingle(out float floatValue));
+ Assert.Equal(expectedFloat, floatValue);
+
+ Assert.Equal(expectedFloat, json.GetSingle());
+ }
+
Assert.True(json.TryGetDouble(out double doubleValue));
Assert.Equal(expectedDouble, doubleValue);
-
- Assert.Equal(expectedFloat, json.GetSingle());
Assert.Equal(expectedDouble, json.GetDouble());
}
}
[Theory]
[InlineData("-2.79769313486232E+308", double.NegativeInfinity)] // double.MinValue - 1
[InlineData("2.79769313486232E+308", double.PositiveInfinity)] // double.MaxValue + 1
- [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework, "Utf8Parser does not support parsing really large float and double values to infinity.")]
public static void TestingTooLargeDoubleConversionToInfinity(string jsonString, double expected)
{
byte[] dataUtf8 = Encoding.UTF8.GetBytes(jsonString);
{
if (json.TokenType == JsonTokenType.Number)
{
- Assert.True(json.TryGetDouble(out double actual));
- Assert.Equal(expected, actual);
+ if (PlatformDetection.IsFullFramework)
+ {
+ // Full framework throws for overflow rather than returning Infinity
+ // This was fixed for .NET Core 3.0 in order to be IEEE 754 compliant
+
+ Assert.False(json.TryGetDouble(out double _));
- Assert.Equal(expected, json.GetDouble());
+ try
+ {
+ json.GetDouble();
+ Assert.True(false, $"Expected {nameof(FormatException)}.");
+ }
+ catch (FormatException)
+ {
+ /* Expected exception */
+ }
+ }
+ else
+ {
+ Assert.True(json.TryGetDouble(out double actual));
+ Assert.Equal(expected, actual);
+
+ Assert.Equal(expected, json.GetDouble());
+ }
}
}