Description
Describe the bug
When serializing and deserializing quantities that use the decimal
type for storage interally, JSON deserialization using the converters from UnitsNet.Serialization.JsonNet may fail with an OverflowException
if the value is too big or too large.
To Reproduce
Steps to reproduce the behavior (just an example):
- Add nuget UnitsNet 4.72.0, UnitsNet.Serialization.JsonNet 4.2.0, Xunit, and FluentAssertions to a .NET Core 3.1 class library project
- Build and run the test code below.
- See 6 failing tests.
using System.Collections.Generic;
using System.Linq;
using FluentAssertions;
using Newtonsoft.Json;
using UnitsNet;
using UnitsNet.Serialization.JsonNet;
using Xunit;
public class QuantitySerializationTests
{
public static IEnumerable<object[]> QuantityZeros { get; } =
Quantity.Infos.Select(
info => new object[]
{
(IQuantity)info.Zero
});
public static IEnumerable<object[]> QuantityMinValues { get; } =
Quantity.Infos.Select(
info => new object[]
{
(IQuantity)info.ValueType.GetProperty("MinValue").GetValue(null)
});
public static IEnumerable<object[]> QuantityMaxValues { get; } =
Quantity.Infos.Select(
info => new object[]
{
(IQuantity)info.ValueType.GetProperty("MaxValue").GetValue(null)
});
[Theory]
[MemberData(nameof(QuantityZeros))]
public void Zero_IsMessagePackSerializable(IQuantity value)
{
var sutClone =
JsonConvert.DeserializeObject(
JsonConvert.SerializeObject(value, Settings),
value.GetType(), Settings);
sutClone.Should().NotBeSameAs(value);
sutClone.Should().BeEquivalentTo(value, o => o.RespectingRuntimeTypes());
}
[Theory]
[MemberData(nameof(QuantityMinValues))]
public void MinValue_IsMessagePackSerializable(IQuantity value)
{
var sutClone =
JsonConvert.DeserializeObject(
JsonConvert.SerializeObject(value, Settings),
value.GetType(), Settings);
sutClone.Should().NotBeSameAs(value);
sutClone.Should().BeEquivalentTo(value, o => o.RespectingRuntimeTypes());
}
[Theory]
[MemberData(nameof(QuantityMaxValues))]
public void MaxValue_IsMessagePackSerializable(IQuantity value)
{
var sutClone =
JsonConvert.DeserializeObject(
JsonConvert.SerializeObject(value, Settings),
value.GetType(), Settings);
sutClone.Should().NotBeSameAs(value);
sutClone.Should().BeEquivalentTo(value, o => o.RespectingRuntimeTypes());
}
private static readonly JsonSerializerSettings Settings = new JsonSerializerSettings
{
TypeNameHandling = TypeNameHandling.Auto,
Converters =
{
new UnitsNetIQuantityJsonConverter(),
new UnitsNetIComparableJsonConverter()
}
};
}
Example failed test result:
Tests.QuantitySerializationTests.MaxValue_IsMessagePackSerializable.MaxValue_IsMessagePackSerializable(value: 7,92e+28 W)
System.OverflowException: Value was either too large or too small for a Decimal.
System.OverflowException
Value was either too large or too small for a Decimal.
at System.Number.ThrowOverflowException(TypeCode type)
at System.Decimal.DecCalc.VarDecFromR8(Double input, DecCalc& result)
at System.Decimal.op_Explicit(Double value)
at UnitsNet.QuantityValue.op_Explicit(QuantityValue number)
at UnitsNet.Power.From(QuantityValue value, PowerUnit fromUnit)
at UnitsNet.Quantity.TryFrom(QuantityValue value, Enum unit, IQuantity& quantity)
at UnitsNet.Quantity.From(QuantityValue value, Enum unit)
at UnitsNet.Serialization.JsonNet.UnitsNetBaseJsonConverter1.ConvertValueUnit(ValueUnit valueUnit) at UnitsNet.Serialization.JsonNet.UnitsNetIQuantityJsonConverter.ReadJson(JsonReader reader, Type objectType, IQuantity existingValue, Boolean hasExistingValue, JsonSerializer > serializer) at Newtonsoft.Json.JsonConverter
1.ReadJson(JsonReader reader, Type objectType, Object existingValue, JsonSerializer serializer)
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.DeserializeConvertable(JsonConverter converter, JsonReader reader, Type objectType, Object existingValue)
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.Deserialize(JsonReader reader, Type objectType, Boolean checkAdditionalContent)
at Newtonsoft.Json.JsonSerializer.DeserializeInternal(JsonReader reader, Type objectType)
at Newtonsoft.Json.JsonSerializer.Deserialize(JsonReader reader, Type objectType)
at Newtonsoft.Json.JsonConvert.DeserializeObject(String value, Type type, JsonSerializerSettings settings)
at Tests.QuantitySerializationTests
Expected behavior
No tests fail.