Skip to content

Commit

Permalink
Merge pull request #992 from filzrev/fix-json-number-serialization
Browse files Browse the repository at this point in the history
fix: floating-point number serialization issue for non-normal values

+semver:fix
  • Loading branch information
EdwardCooke authored Nov 10, 2024
2 parents d2128b2 + eb37834 commit 9495afa
Show file tree
Hide file tree
Showing 2 changed files with 66 additions and 5 deletions.
49 changes: 49 additions & 0 deletions YamlDotNet.Test/Serialization/SerializationTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -920,6 +920,55 @@ public void NullsRoundTrip()
result.MyString.Should().BeNull();
}

[Fact]
public void SerializationOfNumericsAsJsonRountTrip()
{
var serializer = new SerializerBuilder().JsonCompatible().Build();
var deserializer = new DeserializerBuilder().Build();

var data = new
{
FloatValue1 = float.MinValue,
FloatValue2 = float.MaxValue,
FloatValue3 = float.NaN,
FloatValue4 = float.PositiveInfinity,
FloatValue5 = float.NegativeInfinity,
FloatValue6 = 0.0f,
DoubleValue1 = double.MinValue,
DoubleValue2 = double.MaxValue,
DoubleValue3 = double.NaN,
DoubleValue4 = double.PositiveInfinity,
DoubleValue5 = double.NegativeInfinity,
DoubleValue6 = 3.0d,
DecimalValue1 = decimal.MinValue,
DecimalValue2 = decimal.MaxValue,
DecimalValue3 = 1.234567890d,
};

var json = serializer.Serialize(data);

#if NETFRAMEWORK
json.Should().Contain("\"FloatValue3\": \"NaN\"");
json.Should().Contain("\"FloatValue4\": \"Infinity\"");
json.Should().Contain("\"FloatValue5\": \"-Infinity\"");

json.Should().Contain("\"DoubleValue3\": \"NaN\"");
json.Should().Contain("\"DoubleValue4\": \"Infinity\"");
json.Should().Contain("\"DoubleValue5\": \"-Infinity\"");
#else
// Run JSON roundtrip with System.Text.Json and Newtonsoft.Json
var systemTextJson = System.Text.Json.JsonSerializer.Deserialize<System.Text.Json.JsonElement>(json, new System.Text.Json.JsonSerializerOptions { NumberHandling = System.Text.Json.Serialization.JsonNumberHandling.AllowNamedFloatingPointLiterals }).ToString();
var newtonsoftJson = Newtonsoft.Json.JsonConvert.DeserializeObject<Newtonsoft.Json.Linq.JToken>(json).ToString(Newtonsoft.Json.Formatting.None);

// Deserialize JSON with YamlDotNet
var systemTextJsonResult = deserializer.Deserialize<Dictionary<string, object>>(systemTextJson);
var newtonsoftJsonResult = deserializer.Deserialize<Dictionary<string, object>>(newtonsoftJson);

// Assert
systemTextJsonResult.Should().BeEquivalentTo(newtonsoftJsonResult);
#endif
}

[Theory]
[InlineData(typeof(SByteEnum))]
[InlineData(typeof(ByteEnum))]
Expand Down
22 changes: 17 additions & 5 deletions YamlDotNet/Serialization/EventEmitters/JsonEventEmitter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
// SOFTWARE.

using System;
using System.Globalization;
using System.Text.RegularExpressions;
using YamlDotNet.Core;
using YamlDotNet.Core.Events;
Expand All @@ -32,7 +33,6 @@ public sealed class JsonEventEmitter : ChainedEventEmitter
private readonly YamlFormatter formatter;
private readonly INamingConvention enumNamingConvention;
private readonly ITypeInspector typeInspector;
private static readonly Regex NumericRegex = new Regex(@"^-?\d+\.?\d+$", RegexOptions.Compiled);

public JsonEventEmitter(IEventEmitter nextEmitter, YamlFormatter formatter, INamingConvention enumNamingConvention, ITypeInspector typeInspector)
: base(nextEmitter)
Expand Down Expand Up @@ -86,15 +86,27 @@ public override void Emit(ScalarEventInfo eventInfo, IEmitter emitter)
break;

case TypeCode.Single:
case TypeCode.Double:
case TypeCode.Decimal:
eventInfo.RenderedValue = formatter.FormatNumber(value);
var floatValue = (float)value;
eventInfo.RenderedValue = floatValue.ToString("G", CultureInfo.InvariantCulture);
if (float.IsNaN(floatValue) || float.IsInfinity(floatValue))
{
eventInfo.Style = ScalarStyle.DoubleQuoted;
}

if (!NumericRegex.IsMatch(eventInfo.RenderedValue))
break;

case TypeCode.Double:
var doubleValue = (double)value;
eventInfo.RenderedValue = doubleValue.ToString("G", CultureInfo.InvariantCulture);
if (double.IsNaN(doubleValue) || double.IsInfinity(doubleValue))
{
eventInfo.Style = ScalarStyle.DoubleQuoted;
}
break;

case TypeCode.Decimal:
var decimalValue = (decimal)value;
eventInfo.RenderedValue = decimalValue.ToString(CultureInfo.InvariantCulture);
break;

case TypeCode.String:
Expand Down

0 comments on commit 9495afa

Please sign in to comment.