From 7e60aef258adee8368ef3f54c97fd9fa28debfc8 Mon Sep 17 00:00:00 2001 From: Dirk Schuermans Date: Wed, 5 Feb 2020 11:38:05 +0100 Subject: [PATCH 1/4] Reworked UnitsNetJsonConverter according to #732. Also split up serialization and deserialization tests into seperate files for better readability and extracted test objects into their own classes. Consider this an overall clean-up of the test project --- .../Infrastructure/TestObject.cs | 11 + .../TestObjectWithIComparable.cs | 12 + .../TestObjectWithThreeIComparable.cs | 16 + ...TestObjectWithValueAndUnitAsIComparable.cs | 18 + .../TestObjectWithValueAsIComparable.cs | 32 ++ .../UnitsNetJsonBaseTest.cs | 33 ++ .../UnitsNetJsonConverterTests.cs | 431 ------------------ .../UnitsNetJsonDeserializationTests.cs | 216 +++++++++ .../UnitsNetJsonSerializationTests.cs | 133 ++++++ .../Internal/ReflectionHelper.cs | 3 + .../UnitsNetIComparableJsonConverter.cs | 78 ++++ .../UnitsNetJsonBaseConverter.cs | 172 +++++++ .../UnitsNetJsonConverter.cs | 1 + .../UnitsNetJsonIQuantityConverter.cs | 69 +++ 14 files changed, 794 insertions(+), 431 deletions(-) create mode 100644 UnitsNet.Serialization.JsonNet.Tests/Infrastructure/TestObject.cs create mode 100644 UnitsNet.Serialization.JsonNet.Tests/Infrastructure/TestObjectWithIComparable.cs create mode 100644 UnitsNet.Serialization.JsonNet.Tests/Infrastructure/TestObjectWithThreeIComparable.cs create mode 100644 UnitsNet.Serialization.JsonNet.Tests/Infrastructure/TestObjectWithValueAndUnitAsIComparable.cs create mode 100644 UnitsNet.Serialization.JsonNet.Tests/Infrastructure/TestObjectWithValueAsIComparable.cs create mode 100644 UnitsNet.Serialization.JsonNet.Tests/UnitsNetJsonBaseTest.cs delete mode 100644 UnitsNet.Serialization.JsonNet.Tests/UnitsNetJsonConverterTests.cs create mode 100644 UnitsNet.Serialization.JsonNet.Tests/UnitsNetJsonDeserializationTests.cs create mode 100644 UnitsNet.Serialization.JsonNet.Tests/UnitsNetJsonSerializationTests.cs create mode 100644 UnitsNet.Serialization.JsonNet/UnitsNetIComparableJsonConverter.cs create mode 100644 UnitsNet.Serialization.JsonNet/UnitsNetJsonBaseConverter.cs create mode 100644 UnitsNet.Serialization.JsonNet/UnitsNetJsonIQuantityConverter.cs diff --git a/UnitsNet.Serialization.JsonNet.Tests/Infrastructure/TestObject.cs b/UnitsNet.Serialization.JsonNet.Tests/Infrastructure/TestObject.cs new file mode 100644 index 0000000000..a33729602a --- /dev/null +++ b/UnitsNet.Serialization.JsonNet.Tests/Infrastructure/TestObject.cs @@ -0,0 +1,11 @@ +// Licensed under MIT No Attribution, see LICENSE file at the root. +// Copyright 2013 Andreas Gullberg Larsen (andreas.larsen84@gmail.com). Maintained at https://github.com/angularsen/UnitsNet. + +namespace UnitsNet.Serialization.JsonNet.Tests.Infrastructure +{ + public sealed class TestObject + { + public Frequency? NullableFrequency { get; set; } + public Frequency NonNullableFrequency { get; set; } + } +} diff --git a/UnitsNet.Serialization.JsonNet.Tests/Infrastructure/TestObjectWithIComparable.cs b/UnitsNet.Serialization.JsonNet.Tests/Infrastructure/TestObjectWithIComparable.cs new file mode 100644 index 0000000000..41473629f9 --- /dev/null +++ b/UnitsNet.Serialization.JsonNet.Tests/Infrastructure/TestObjectWithIComparable.cs @@ -0,0 +1,12 @@ +// Licensed under MIT No Attribution, see LICENSE file at the root. +// Copyright 2013 Andreas Gullberg Larsen (andreas.larsen84@gmail.com). Maintained at https://github.com/angularsen/UnitsNet. + +using System; + +namespace UnitsNet.Serialization.JsonNet.Tests.Infrastructure +{ + public sealed class TestObjectWithIComparable + { + public IComparable Value { get; set; } + } +} diff --git a/UnitsNet.Serialization.JsonNet.Tests/Infrastructure/TestObjectWithThreeIComparable.cs b/UnitsNet.Serialization.JsonNet.Tests/Infrastructure/TestObjectWithThreeIComparable.cs new file mode 100644 index 0000000000..7806e248fd --- /dev/null +++ b/UnitsNet.Serialization.JsonNet.Tests/Infrastructure/TestObjectWithThreeIComparable.cs @@ -0,0 +1,16 @@ +// Licensed under MIT No Attribution, see LICENSE file at the root. +// Copyright 2013 Andreas Gullberg Larsen (andreas.larsen84@gmail.com). Maintained at https://github.com/angularsen/UnitsNet. + +using System; + +namespace UnitsNet.Serialization.JsonNet.Tests.Infrastructure +{ + public sealed class TestObjectWithThreeIComparable + { + public IComparable Value1 { get; set; } + + public IComparable Value2 { get; set; } + + public IComparable Value3 { get; set; } + } +} diff --git a/UnitsNet.Serialization.JsonNet.Tests/Infrastructure/TestObjectWithValueAndUnitAsIComparable.cs b/UnitsNet.Serialization.JsonNet.Tests/Infrastructure/TestObjectWithValueAndUnitAsIComparable.cs new file mode 100644 index 0000000000..6ec8d14b86 --- /dev/null +++ b/UnitsNet.Serialization.JsonNet.Tests/Infrastructure/TestObjectWithValueAndUnitAsIComparable.cs @@ -0,0 +1,18 @@ +// Licensed under MIT No Attribution, see LICENSE file at the root. +// Copyright 2013 Andreas Gullberg Larsen (andreas.larsen84@gmail.com). Maintained at https://github.com/angularsen/UnitsNet. + +using System; + +namespace UnitsNet.Serialization.JsonNet.Tests.Infrastructure +{ + public sealed class TestObjectWithValueAndUnitAsIComparable : IComparable + { + public double Value { get; set; } + public string Unit { get; set; } + + public int CompareTo(object obj) + { + return ((IComparable)Value).CompareTo(obj); + } + } +} diff --git a/UnitsNet.Serialization.JsonNet.Tests/Infrastructure/TestObjectWithValueAsIComparable.cs b/UnitsNet.Serialization.JsonNet.Tests/Infrastructure/TestObjectWithValueAsIComparable.cs new file mode 100644 index 0000000000..b3839f8776 --- /dev/null +++ b/UnitsNet.Serialization.JsonNet.Tests/Infrastructure/TestObjectWithValueAsIComparable.cs @@ -0,0 +1,32 @@ +// Licensed under MIT No Attribution, see LICENSE file at the root. +// Copyright 2013 Andreas Gullberg Larsen (andreas.larsen84@gmail.com). Maintained at https://github.com/angularsen/UnitsNet. + +using System; + +namespace UnitsNet.Serialization.JsonNet.Tests.Infrastructure +{ + public sealed class TestObjectWithValueAsIComparable : IComparable + { + public int Value { get; set; } + + public int CompareTo(object obj) + { + return ((IComparable)Value).CompareTo(obj); + } + + // Needed for verifying that the deserialized object is the same, should not affect the serialization code + public override bool Equals(object obj) + { + if (obj == null || GetType() != obj.GetType()) + { + return false; + } + return Value.Equals(((TestObjectWithValueAsIComparable)obj).Value); + } + + public override int GetHashCode() + { + return Value.GetHashCode(); + } + } +} diff --git a/UnitsNet.Serialization.JsonNet.Tests/UnitsNetJsonBaseTest.cs b/UnitsNet.Serialization.JsonNet.Tests/UnitsNetJsonBaseTest.cs new file mode 100644 index 0000000000..c2e1502924 --- /dev/null +++ b/UnitsNet.Serialization.JsonNet.Tests/UnitsNetJsonBaseTest.cs @@ -0,0 +1,33 @@ +// Licensed under MIT No Attribution, see LICENSE file at the root. +// Copyright 2013 Andreas Gullberg Larsen (andreas.larsen84@gmail.com). Maintained at https://github.com/angularsen/UnitsNet. + +using Newtonsoft.Json; + +namespace UnitsNet.Serialization.JsonNet.Tests +{ + public abstract class UnitsNetJsonBaseTest + { + private readonly JsonSerializerSettings jsonSerializerSettings; + + protected UnitsNetJsonBaseTest() + { + jsonSerializerSettings = new JsonSerializerSettings {Formatting = Formatting.Indented}; + jsonSerializerSettings.Converters.Add(new UnitsNetJsonIQuantityConverter()); + jsonSerializerSettings.Converters.Add(new UnitsNetIComparableJsonConverter()); + } + + protected string SerializeObject(object obj, TypeNameHandling typeNameHandling = TypeNameHandling.None) + { + jsonSerializerSettings.TypeNameHandling = typeNameHandling; + + return JsonConvert.SerializeObject(obj, jsonSerializerSettings).Replace("\r\n", "\n"); + } + + protected T DeserializeObject(string json, TypeNameHandling typeNameHandling = TypeNameHandling.None) + { + jsonSerializerSettings.TypeNameHandling = typeNameHandling; + + return JsonConvert.DeserializeObject(json, jsonSerializerSettings); + } + } +} diff --git a/UnitsNet.Serialization.JsonNet.Tests/UnitsNetJsonConverterTests.cs b/UnitsNet.Serialization.JsonNet.Tests/UnitsNetJsonConverterTests.cs deleted file mode 100644 index 8cb662fe22..0000000000 --- a/UnitsNet.Serialization.JsonNet.Tests/UnitsNetJsonConverterTests.cs +++ /dev/null @@ -1,431 +0,0 @@ -// Licensed under MIT No Attribution, see LICENSE file at the root. -// Copyright 2013 Andreas Gullberg Larsen (andreas.larsen84@gmail.com). Maintained at https://github.com/angularsen/UnitsNet. - -using System; -using Newtonsoft.Json; -using Xunit; - -namespace UnitsNet.Serialization.JsonNet.Tests -{ - public class UnitsNetJsonConverterTests - { - private readonly JsonSerializerSettings _jsonSerializerSettings; - - protected UnitsNetJsonConverterTests() - { - _jsonSerializerSettings = new JsonSerializerSettings {Formatting = Formatting.Indented}; - _jsonSerializerSettings.Converters.Add(new UnitsNetJsonConverter()); - } - - private string SerializeObject(object obj) - { - return JsonConvert.SerializeObject(obj, _jsonSerializerSettings).Replace("\r\n", "\n"); - } - - private T DeserializeObject(string json) - { - return JsonConvert.DeserializeObject(json, _jsonSerializerSettings); - } - - public class Serialize : UnitsNetJsonConverterTests - { - [Fact] - public void Information_CanSerializeVeryLargeValues() - { - Information i = Information.FromExabytes(1E+9); - var expectedJson = "{\n \"Unit\": \"InformationUnit.Exabyte\",\n \"Value\": 1000000000.0\n}"; - - string json = SerializeObject(i); - - Assert.Equal(expectedJson, json); - } - - [Fact] - public void Mass_ExpectConstructedValueAndUnit() - { - Mass mass = Mass.FromPounds(200); - var expectedJson = "{\n \"Unit\": \"MassUnit.Pound\",\n \"Value\": 200.0\n}"; - - string json = SerializeObject(mass); - - Assert.Equal(expectedJson, json); - } - - [Fact] - public void Information_ExpectConstructedValueAndUnit() - { - Information quantity = Information.FromKilobytes(54); - var expectedJson = "{\n \"Unit\": \"InformationUnit.Kilobyte\",\n \"Value\": 54.0\n}"; - - string json = SerializeObject(quantity); - - Assert.Equal(expectedJson, json); - } - - [Fact] - public void NonNullNullableValue_ExpectJsonUnaffected() - { - Mass? nullableMass = Mass.FromKilograms(10); - var expectedJson = "{\n \"Unit\": \"MassUnit.Kilogram\",\n \"Value\": 10.0\n}"; - - string json = SerializeObject(nullableMass); - - // There shouldn't be any change in the JSON for the non-null nullable value. - Assert.Equal(expectedJson, json); - } - - [Fact] - public void NonNullNullableValueNestedInObject_ExpectJsonUnaffected() - { - var testObj = new TestObj - { - NullableFrequency = Frequency.FromHertz(10), - NonNullableFrequency = Frequency.FromHertz(10) - }; - // Ugly manually formatted JSON string is used because string literals with newlines are rendered differently - // on the build server (i.e. the build server uses '\r' instead of '\n') - string expectedJson = "{\n" + - " \"NullableFrequency\": {\n" + - " \"Unit\": \"FrequencyUnit.Hertz\",\n" + - " \"Value\": 10.0\n" + - " },\n" + - " \"NonNullableFrequency\": {\n" + - " \"Unit\": \"FrequencyUnit.Hertz\",\n" + - " \"Value\": 10.0\n" + - " }\n" + - "}"; - - string json = SerializeObject(testObj); - - Assert.Equal(expectedJson, json); - } - - [Fact] - public void NullValue_ExpectJsonContainsNullString() - { - string json = SerializeObject(null); - Assert.Equal("null", json); - } - - [Fact] - public void Ratio_ExpectDecimalFractionsUsedAsBaseValueAndUnit() - { - Ratio ratio = Ratio.FromPartsPerThousand(250); - var expectedJson = "{\n \"Unit\": \"RatioUnit.PartPerThousand\",\n \"Value\": 250.0\n}"; - - string json = SerializeObject(ratio); - - Assert.Equal(expectedJson, json); - } - - [Fact] - public void ArrayValue_ExpectJsonArray() - { - Frequency[] testObj = { Frequency.FromHertz(10), Frequency.FromHertz(10) }; - - string expectedJson = "[\n" + - " {\n" + - " \"Unit\": \"FrequencyUnit.Hertz\",\n" + - " \"Value\": 10.0\n" + - " },\n" + - " {\n" + - " \"Unit\": \"FrequencyUnit.Hertz\",\n" + - " \"Value\": 10.0\n" + - " }\n" + - "]"; - - string json = SerializeObject(testObj); - - Assert.Equal(expectedJson, json); - } - - [Fact] - public void EmptyArrayValue_ExpectJsonArray() - { - Frequency[] testObj = new Frequency[0]; - - string expectedJson = "[]"; - - string json = SerializeObject(testObj); - Assert.Equal(expectedJson, json); - } - } - - public class Deserialize : UnitsNetJsonConverterTests - { - [Fact] - public void Information_CanDeserializeVeryLargeValues() - { - Information original = Information.FromExabytes(1E+9); - string json = SerializeObject(original); - var deserialized = DeserializeObject(json); - - Assert.Equal(original, deserialized); - } - - [Fact] - public void Mass_ExpectJsonCorrectlyDeserialized() - { - Mass originalMass = Mass.FromKilograms(33.33); - string json = SerializeObject(originalMass); - - var deserializedMass = DeserializeObject(json); - - Assert.Equal(originalMass, deserializedMass); - } - - [Fact] - public void NonNullNullableValue_ExpectValueDeserializedCorrectly() - { - Mass? nullableMass = Mass.FromKilograms(10); - string json = SerializeObject(nullableMass); - - Mass? deserializedNullableMass = DeserializeObject(json); - - Assert.Equal(nullableMass.Value, deserializedNullableMass); - } - - [Fact] - public void NonNullNullableValueNestedInObject_ExpectValueDeserializedCorrectly() - { - var testObj = new TestObj - { - NullableFrequency = Frequency.FromHertz(10), - NonNullableFrequency = Frequency.FromHertz(10) - }; - string json = SerializeObject(testObj); - - var deserializedTestObj = DeserializeObject(json); - - Assert.Equal(testObj.NullableFrequency, deserializedTestObj.NullableFrequency); - } - - [Fact] - public void NullValue_ExpectNullReturned() - { - string json = SerializeObject(null); - var deserializedNullMass = DeserializeObject(json); - - Assert.Null(deserializedNullMass); - } - - [Fact] - public void NullValueNestedInObject_ExpectValueDeserializedToNullCorrectly() - { - var testObj = new TestObj - { - NullableFrequency = null, - NonNullableFrequency = Frequency.FromHertz(10) - }; - string json = SerializeObject(testObj); - - var deserializedTestObj = DeserializeObject(json); - - Assert.Null(deserializedTestObj.NullableFrequency); - } - - [Fact] - public void UnitEnumChangedAfterSerialization_ExpectUnitCorrectlyDeserialized() - { - Mass originalMass = Mass.FromKilograms(33.33); - string json = SerializeObject(originalMass); - // Someone manually changed the serialized JSON string to 1000 grams. - json = json.Replace("33.33", "1000"); - json = json.Replace("MassUnit.Kilogram", "MassUnit.Gram"); - - var deserializedMass = DeserializeObject(json); - - // The original value serialized was 33.33 kg, but someone edited the JSON to be 1000 g. We expect the JSON is - // still deserializable, and the correct value of 1000 g is obtained. - Assert.Equal(1000, deserializedMass.Grams); - } - - [Fact] - public void UnitInIComparable_ExpectUnitCorrectlyDeserialized() - { - TestObjWithIComparable testObjWithIComparable = new TestObjWithIComparable() - { - Value = Power.FromWatts(10) - }; - JsonSerializerSettings jsonSerializerSettings = CreateJsonSerializerSettings(); - - string json = JsonConvert.SerializeObject(testObjWithIComparable, jsonSerializerSettings); - - var deserializedTestObject = JsonConvert.DeserializeObject(json,jsonSerializerSettings); - - Assert.Equal(typeof(Power), deserializedTestObject.Value.GetType()); - Assert.Equal(Power.FromWatts(10), (Power)deserializedTestObject.Value); - } - - [Fact] - public void DoubleInIComparable_ExpectUnitCorrectlyDeserialized() - { - TestObjWithIComparable testObjWithIComparable = new TestObjWithIComparable() - { - Value = 10.0 - }; - JsonSerializerSettings jsonSerializerSettings = CreateJsonSerializerSettings(); - - string json = JsonConvert.SerializeObject(testObjWithIComparable, jsonSerializerSettings); - - var deserializedTestObject = JsonConvert.DeserializeObject(json, jsonSerializerSettings); - - Assert.Equal(typeof(double), deserializedTestObject.Value.GetType()); - Assert.Equal(10d, (double)deserializedTestObject.Value); - } - - [Fact] - public void ClassInIComparable_ExpectUnitCorrectlyDeserialized() - { - TestObjWithIComparable testObjWithIComparable = new TestObjWithIComparable() - { - Value = new ComparableClass() { Value = 10 } - }; - JsonSerializerSettings jsonSerializerSettings = CreateJsonSerializerSettings(); - - string json = JsonConvert.SerializeObject(testObjWithIComparable, jsonSerializerSettings); - var deserializedTestObject = JsonConvert.DeserializeObject(json, jsonSerializerSettings); - - Assert.Equal(typeof(ComparableClass), deserializedTestObject.Value.GetType()); - Assert.Equal(10d, ((ComparableClass) deserializedTestObject.Value).Value); - } - - [Fact] - public void OtherObjectWithUnitAndValue_ExpectCorrectResturnValues() - { - TestObjWithValueAndUnit testObjWithValueAndUnit = new TestObjWithValueAndUnit() - { - Value = 5, - Unit = "Test", - }; - JsonSerializerSettings jsonSerializerSettings = CreateJsonSerializerSettings(); - - string json = JsonConvert.SerializeObject(testObjWithValueAndUnit, jsonSerializerSettings); - TestObjWithValueAndUnit deserializedTestObject = JsonConvert.DeserializeObject(json, jsonSerializerSettings); - - Assert.Equal(typeof(double), deserializedTestObject.Value.GetType()); - Assert.Equal(5d, deserializedTestObject.Value); - Assert.Equal("Test", deserializedTestObject.Unit); - } - - [Fact] - public void ThreeObjectsInIComparableWithDifferentValues_ExpectAllCorrectlyDeserialized() - { - TestObjWithThreeIComparable testObjWithIComparable = new TestObjWithThreeIComparable() - { - Value1 = 10.0, - Value2 = Power.FromWatts(19), - Value3 = new ComparableClass() { Value = 10 }, - }; - JsonSerializerSettings jsonSerializerSettings = CreateJsonSerializerSettings(); - - string json = JsonConvert.SerializeObject(testObjWithIComparable, jsonSerializerSettings); - var deserializedTestObject = JsonConvert.DeserializeObject(json, jsonSerializerSettings); - - Assert.Equal(typeof(double), deserializedTestObject.Value1.GetType()); - Assert.Equal(10d, deserializedTestObject.Value1); - Assert.Equal(typeof(Power), deserializedTestObject.Value2.GetType()); - Assert.Equal(Power.FromWatts(19), deserializedTestObject.Value2); - Assert.Equal(typeof(ComparableClass), deserializedTestObject.Value3.GetType()); - Assert.Equal(testObjWithIComparable.Value3, deserializedTestObject.Value3); - } - - [Fact] - public void ArrayOfUnits_ExpectCorrectlyDeserialized() - { - Frequency[] expected = { Frequency.FromHertz(10), Frequency.FromHertz(10) }; - - string json = "[\n" + - " {\n" + - " \"Unit\": \"FrequencyUnit.Hertz\",\n" + - " \"Value\": 10.0\n" + - " },\n" + - " {\n" + - " \"Unit\": \"FrequencyUnit.Hertz\",\n" + - " \"Value\": 10.0\n" + - " }\n" + - "]"; - - Frequency[] result = DeserializeObject(json); - - Assert.Equal(expected, result); - } - - [Fact] - public void EmptyArray_ExpectCorreclyDeserialized() - { - string json = "[]"; - - Frequency[] result = DeserializeObject(json); - - Assert.Empty(result); - } - - private static JsonSerializerSettings CreateJsonSerializerSettings() - { - var jsonSerializerSettings = new JsonSerializerSettings() - { - Formatting = Formatting.Indented, - TypeNameHandling = TypeNameHandling.Auto - }; - jsonSerializerSettings.Converters.Add(new UnitsNetJsonConverter()); - return jsonSerializerSettings; - } - } - - private class TestObj - { - public Frequency? NullableFrequency { get; set; } - public Frequency NonNullableFrequency { get; set; } - } - - private class TestObjWithValueAndUnit : IComparable - { - public double Value { get; set; } - public string Unit { get; set; } - - public int CompareTo(object obj) - { - return ((IComparable)Value).CompareTo(obj); - } - } - - private class ComparableClass : IComparable - { - public int Value { get; set; } - public int CompareTo(object obj) - { - return ((IComparable)Value).CompareTo(obj); - } - - // Needed for virfying that the deserialized object is the same, should not affect the serilization code - public override bool Equals(object obj) - { - if (obj == null || GetType() != obj.GetType()) - { - return false; - } - return Value.Equals(((ComparableClass)obj).Value); - } - - public override int GetHashCode() - { - return Value.GetHashCode(); - } - } - - private class TestObjWithIComparable - { - public IComparable Value { get; set; } - } - - private class TestObjWithThreeIComparable - { - public IComparable Value1 { get; set; } - - public IComparable Value2 { get; set; } - - public IComparable Value3 { get; set; } - } - } -} diff --git a/UnitsNet.Serialization.JsonNet.Tests/UnitsNetJsonDeserializationTests.cs b/UnitsNet.Serialization.JsonNet.Tests/UnitsNetJsonDeserializationTests.cs new file mode 100644 index 0000000000..3ce95c18fb --- /dev/null +++ b/UnitsNet.Serialization.JsonNet.Tests/UnitsNetJsonDeserializationTests.cs @@ -0,0 +1,216 @@ +// Licensed under MIT No Attribution, see LICENSE file at the root. +// Copyright 2013 Andreas Gullberg Larsen (andreas.larsen84@gmail.com). Maintained at https://github.com/angularsen/UnitsNet. + +using Newtonsoft.Json; +using UnitsNet.Serialization.JsonNet.Tests.Infrastructure; +using Xunit; + +namespace UnitsNet.Serialization.JsonNet.Tests +{ + public sealed class UnitsNetJsonDeserializationTests : UnitsNetJsonBaseTest + { + [Fact] + public void Information_CanDeserializeVeryLargeValues() + { + var original = Information.FromExabytes(1E+9); + var json = SerializeObject(original); + var deserialized = DeserializeObject(json); + + Assert.Equal(original, deserialized); + } + + [Fact] + public void Mass_ExpectJsonCorrectlyDeserialized() + { + var originalMass = Mass.FromKilograms(33.33); + var json = SerializeObject(originalMass); + + var deserializedMass = DeserializeObject(json); + + Assert.Equal(originalMass, deserializedMass); + } + + [Fact] + public void NonNullNullableValue_ExpectValueDeserializedCorrectly() + { + Mass? nullableMass = Mass.FromKilograms(10); + var json = SerializeObject(nullableMass); + + var deserializedNullableMass = DeserializeObject(json); + + Assert.Equal(nullableMass.Value, deserializedNullableMass); + } + + [Fact] + public void NonNullNullableValueNestedInObject_ExpectValueDeserializedCorrectly() + { + var testObj = new TestObject() + { + NullableFrequency = Frequency.FromHertz(10), + NonNullableFrequency = Frequency.FromHertz(10) + }; + var json = SerializeObject(testObj); + + var deserializedTestObj = DeserializeObject(json); + + Assert.Equal(testObj.NullableFrequency, deserializedTestObj.NullableFrequency); + } + + [Fact] + public void NullValue_ExpectNullReturned() + { + var json = SerializeObject(null); + var deserializedNullMass = DeserializeObject(json); + + Assert.Null(deserializedNullMass); + } + + [Fact] + public void NullValueNestedInObject_ExpectValueDeserializedToNullCorrectly() + { + var testObj = new TestObject() + { + NullableFrequency = null, + NonNullableFrequency = Frequency.FromHertz(10) + }; + var json = SerializeObject(testObj); + + var deserializedTestObj = DeserializeObject(json); + + Assert.Null(deserializedTestObj.NullableFrequency); + } + + [Fact] + public void UnitEnumChangedAfterSerialization_ExpectUnitCorrectlyDeserialized() + { + var originalMass = Mass.FromKilograms(33.33); + var json = SerializeObject(originalMass); + + // Someone manually changed the serialized JSON string to 1000 grams. + json = json.Replace("33.33", "1000"); + json = json.Replace("MassUnit.Kilogram", "MassUnit.Gram"); + + var deserializedMass = DeserializeObject(json); + + // The original value serialized was 33.33 kg, but someone edited the JSON to be 1000 g. We expect the JSON is + // still deserializable, and the correct value of 1000 g is obtained. + Assert.Equal(1000, deserializedMass.Grams); + } + + [Fact] + public void UnitInIComparable_ExpectUnitCorrectlyDeserialized() + { + var testObjWithIComparable = new TestObjectWithIComparable() + { + Value = Power.FromWatts(10) + }; + + var json = SerializeObject(testObjWithIComparable); + + var deserializedTestObject = DeserializeObject(json); + + Assert.IsType(deserializedTestObject.Value); + Assert.Equal(Power.FromWatts(10), (Power)deserializedTestObject.Value); + } + + [Fact] + public void DoubleInIComparable_ExpectUnitCorrectlyDeserialized() + { + var testObjWithIComparable = new TestObjectWithIComparable() + { + Value = 10.0 + }; + + var json = SerializeObject(testObjWithIComparable); + var deserializedTestObject = DeserializeObject(json); + + Assert.IsType(deserializedTestObject.Value); + Assert.Equal(10d, (double)deserializedTestObject.Value); + } + + [Fact] + public void ClassInIComparable_ExpectUnitCorrectlyDeserialized() + { + var testObjWithIComparable = new TestObjectWithIComparable() + { + Value = new TestObjectWithValueAsIComparable() { Value = 10 } + }; + + var json = SerializeObject(testObjWithIComparable, TypeNameHandling.Auto); + var deserializedTestObject = DeserializeObject(json, TypeNameHandling.Auto); + + Assert.IsType(deserializedTestObject.Value); + Assert.Equal(10d, ((TestObjectWithValueAsIComparable) deserializedTestObject.Value).Value); + } + + [Fact] + public void OtherObjectWithUnitAndValue_ExpectCorrectReturnValues() + { + var testObjWithValueAndUnit = new TestObjectWithValueAndUnitAsIComparable() + { + Value = 5, + Unit = "Test", + }; + + var json = SerializeObject(testObjWithValueAndUnit); + var deserializedTestObject = DeserializeObject(json); + + Assert.IsType(deserializedTestObject.Value); + Assert.Equal(5d, deserializedTestObject.Value); + Assert.Equal("Test", deserializedTestObject.Unit); + } + + [Fact] + public void ThreeObjectsInIComparableWithDifferentValues_ExpectAllCorrectlyDeserialized() + { + var testObjWithIComparable = new TestObjectWithThreeIComparable() + { + Value1 = 10.0, + Value2 = Power.FromWatts(19), + Value3 = new TestObjectWithValueAsIComparable() { Value = 10 }, + }; + var json = SerializeObject(testObjWithIComparable, TypeNameHandling.Auto); + var deserializedTestObject = DeserializeObject(json, TypeNameHandling.Auto); + + Assert.IsType(deserializedTestObject.Value1); + Assert.Equal(10d, deserializedTestObject.Value1); + + Assert.IsType(deserializedTestObject.Value2); + Assert.Equal(Power.FromWatts(19), deserializedTestObject.Value2); + + Assert.IsType(deserializedTestObject.Value3); + Assert.Equal(testObjWithIComparable.Value3, deserializedTestObject.Value3); + } + + [Fact] + public void ArrayOfUnits_ExpectCorrectlyDeserialized() + { + Frequency[] expected = { Frequency.FromHertz(10), Frequency.FromHertz(10) }; + + var json = "[\n" + + " {\n" + + " \"Unit\": \"FrequencyUnit.Hertz\",\n" + + " \"Value\": 10.0\n" + + " },\n" + + " {\n" + + " \"Unit\": \"FrequencyUnit.Hertz\",\n" + + " \"Value\": 10.0\n" + + " }\n" + + "]"; + + var result = DeserializeObject(json); + + Assert.Equal(expected, result); + } + + [Fact] + public void EmptyArray_ExpectCorrectlyDeserialized() + { + var json = "[]"; + + var result = DeserializeObject(json); + + Assert.Empty(result); + } + } +} diff --git a/UnitsNet.Serialization.JsonNet.Tests/UnitsNetJsonSerializationTests.cs b/UnitsNet.Serialization.JsonNet.Tests/UnitsNetJsonSerializationTests.cs new file mode 100644 index 0000000000..879a5c6942 --- /dev/null +++ b/UnitsNet.Serialization.JsonNet.Tests/UnitsNetJsonSerializationTests.cs @@ -0,0 +1,133 @@ +// Licensed under MIT No Attribution, see LICENSE file at the root. +// Copyright 2013 Andreas Gullberg Larsen (andreas.larsen84@gmail.com). Maintained at https://github.com/angularsen/UnitsNet. + +using UnitsNet.Serialization.JsonNet.Tests.Infrastructure; +using Xunit; + +namespace UnitsNet.Serialization.JsonNet.Tests +{ + public sealed class UnitsNetJsonSerializationTests : UnitsNetJsonBaseTest + { + [Fact] + public void Information_CanSerializeVeryLargeValues() + { + Information i = Information.FromExabytes(1E+9); + var expectedJson = "{\n \"Unit\": \"InformationUnit.Exabyte\",\n \"Value\": 1000000000.0\n}"; + + string json = SerializeObject(i); + + Assert.Equal(expectedJson, json); + } + + [Fact] + public void Mass_ExpectConstructedValueAndUnit() + { + Mass mass = Mass.FromPounds(200); + var expectedJson = "{\n \"Unit\": \"MassUnit.Pound\",\n \"Value\": 200.0\n}"; + + string json = SerializeObject(mass); + + Assert.Equal(expectedJson, json); + } + + [Fact] + public void Information_ExpectConstructedValueAndUnit() + { + Information quantity = Information.FromKilobytes(54); + var expectedJson = "{\n \"Unit\": \"InformationUnit.Kilobyte\",\n \"Value\": 54.0\n}"; + + string json = SerializeObject(quantity); + + Assert.Equal(expectedJson, json); + } + + [Fact] + public void NonNullNullableValue_ExpectJsonUnaffected() + { + Mass? nullableMass = Mass.FromKilograms(10); + var expectedJson = "{\n \"Unit\": \"MassUnit.Kilogram\",\n \"Value\": 10.0\n}"; + + string json = SerializeObject(nullableMass); + + // There shouldn't be any change in the JSON for the non-null nullable value. + Assert.Equal(expectedJson, json); + } + + [Fact] + public void NonNullNullableValueNestedInObject_ExpectJsonUnaffected() + { + var testObj = new TestObject() + { + NullableFrequency = Frequency.FromHertz(10), + NonNullableFrequency = Frequency.FromHertz(10) + }; + + // Ugly manually formatted JSON string is used because string literals with newlines are rendered differently + // on the build server (i.e. the build server uses '\r' instead of '\n') + string expectedJson = "{\n" + + " \"NullableFrequency\": {\n" + + " \"Unit\": \"FrequencyUnit.Hertz\",\n" + + " \"Value\": 10.0\n" + + " },\n" + + " \"NonNullableFrequency\": {\n" + + " \"Unit\": \"FrequencyUnit.Hertz\",\n" + + " \"Value\": 10.0\n" + + " }\n" + + "}"; + + string json = SerializeObject(testObj); + + Assert.Equal(expectedJson, json); + } + + [Fact] + public void NullValue_ExpectJsonContainsNullString() + { + string json = SerializeObject(null); + Assert.Equal("null", json); + } + + [Fact] + public void Ratio_ExpectDecimalFractionsUsedAsBaseValueAndUnit() + { + Ratio ratio = Ratio.FromPartsPerThousand(250); + var expectedJson = "{\n \"Unit\": \"RatioUnit.PartPerThousand\",\n \"Value\": 250.0\n}"; + + string json = SerializeObject(ratio); + + Assert.Equal(expectedJson, json); + } + + [Fact] + public void ArrayValue_ExpectJsonArray() + { + Frequency[] testObj = { Frequency.FromHertz(10), Frequency.FromHertz(10) }; + + string expectedJson = "[\n" + + " {\n" + + " \"Unit\": \"FrequencyUnit.Hertz\",\n" + + " \"Value\": 10.0\n" + + " },\n" + + " {\n" + + " \"Unit\": \"FrequencyUnit.Hertz\",\n" + + " \"Value\": 10.0\n" + + " }\n" + + "]"; + + string json = SerializeObject(testObj); + + Assert.Equal(expectedJson, json); + } + + [Fact] + public void EmptyArrayValue_ExpectJsonArray() + { + Frequency[] testObj = new Frequency[0]; + + string expectedJson = "[]"; + + string json = SerializeObject(testObj); + Assert.Equal(expectedJson, json); + } + } +} diff --git a/UnitsNet.Serialization.JsonNet/Internal/ReflectionHelper.cs b/UnitsNet.Serialization.JsonNet/Internal/ReflectionHelper.cs index 930acb1080..c38435e291 100644 --- a/UnitsNet.Serialization.JsonNet/Internal/ReflectionHelper.cs +++ b/UnitsNet.Serialization.JsonNet/Internal/ReflectionHelper.cs @@ -7,8 +7,10 @@ namespace UnitsNet.Serialization.JsonNet.Internal /// /// Helper for dealing with reflection, abstracting API differences between old and new .NET framework. /// + [Obsolete("The JsonConverter(s) for UnitsNet no longer relies on reflection")] internal static class ReflectionHelper { + [Obsolete("The JsonConverter(s) for UnitsNet no longer relies on reflection")] internal static PropertyInfo GetProperty(this Type type, string name) { #if (NET40 || NET35 || NET20 || SILVERLIGHT) @@ -19,6 +21,7 @@ internal static PropertyInfo GetProperty(this Type type, string name) #endif } + [Obsolete("The JsonConverter(s) for UnitsNet no longer relies on reflection")] internal static IEnumerable GetDeclaredMethods(this Type someType) { Type t = someType; diff --git a/UnitsNet.Serialization.JsonNet/UnitsNetIComparableJsonConverter.cs b/UnitsNet.Serialization.JsonNet/UnitsNetIComparableJsonConverter.cs new file mode 100644 index 0000000000..8550c20a98 --- /dev/null +++ b/UnitsNet.Serialization.JsonNet/UnitsNetIComparableJsonConverter.cs @@ -0,0 +1,78 @@ +// Licensed under MIT No Attribution, see LICENSE file at the root. +// Copyright 2013 Andreas Gullberg Larsen (andreas.larsen84@gmail.com). Maintained at https://github.com/angularsen/UnitsNet. + +using System; +using JetBrains.Annotations; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; + +namespace UnitsNet.Serialization.JsonNet +{ + /// + /// + /// JSON.Net converter for IComparable properties that are actually UnitsNet units. + /// Use with caution, as this might interfere with any other converters for IComparable. + /// Must be registered in the collection -after- + /// Should only be used when UnitsNet types are assigned to properties of type IComparable. + /// Requires TypeNameHandling on to be set to something other than so that it outputs $type when serializing. + /// + public sealed class UnitsNetIComparableJsonConverter : UnitsNetJsonBaseConverter + { + /// + /// Indicates if this JsonConverter is capable of writing JSON + /// + public override bool CanWrite => false; + + /// + /// Serialize an IComparable to JSON. + /// Not used. + /// + /// The writer to use + /// The value to serialize + /// The serializer to use + public override void WriteJson(JsonWriter writer, IComparable value, JsonSerializer serializer) + { + throw new NotImplementedException("Serialization of IComparable is handled by default serialization"); + } + + /// + /// Attempts to deserialize a JSON string as UnitsNet type, assigned to property of type IComparable + /// + /// The JsonReader to access the JSON data to read + /// The type of object to deserialize + /// An existing value, if any (which is ignored) + /// Indicates if there is an existing value that should be updated (which is ignored) + /// The JsonSerializer to use for deserialization + /// A deserialized IComparable + public override IComparable ReadJson([NotNull] JsonReader reader, Type objectType, IComparable existingValue, bool hasExistingValue, + [NotNull] JsonSerializer serializer) + { + reader = reader ?? throw new ArgumentNullException(nameof(reader)); + serializer = serializer ?? throw new ArgumentNullException(nameof(serializer)); + + if (reader.TokenType == JsonToken.Null) + { + return null; + } + + var localSerializer = CreateLocalSerializer(serializer, this); + + var token = JToken.Load(reader); + + // If objectType is not IComparable but a type that implements IComparable, deserialize directly as this type instead. + if (objectType != typeof(IComparable)) + { + return token.ToObject(objectType, localSerializer) as IComparable; + } + + var valueUnit = ReadValueUnit(token); + + if (valueUnit == null) + { + return token.ToObject(localSerializer); + } + + return ConvertValueUnit(valueUnit) as IComparable; + } + } +} diff --git a/UnitsNet.Serialization.JsonNet/UnitsNetJsonBaseConverter.cs b/UnitsNet.Serialization.JsonNet/UnitsNetJsonBaseConverter.cs new file mode 100644 index 0000000000..1d6faecdc1 --- /dev/null +++ b/UnitsNet.Serialization.JsonNet/UnitsNetJsonBaseConverter.cs @@ -0,0 +1,172 @@ +// Licensed under MIT No Attribution, see LICENSE file at the root. +// Copyright 2013 Andreas Gullberg Larsen (andreas.larsen84@gmail.com). Maintained at https://github.com/angularsen/UnitsNet. + +using System; +using System.Linq; +using JetBrains.Annotations; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; + +namespace UnitsNet.Serialization.JsonNet +{ + /// + /// Base converter for serializing and deserializing UnitsNet types to and from JSON. + /// Contains shared functionality used by and + /// + /// + public abstract class UnitsNetJsonBaseConverter : JsonConverter + { + /// + /// Reads the "Unit" and "Value" properties from a JSON string + /// + /// The JSON data to read from + /// A + protected ValueUnit ReadValueUnit(JToken jsonToken) + { + if (!jsonToken.HasValues) + { + return null; + } + + var jsonObject = (JObject) jsonToken; + + var unit = jsonObject.GetValue(nameof(ValueUnit.Unit), StringComparison.OrdinalIgnoreCase); + var value = jsonObject.GetValue(nameof(ValueUnit.Value), StringComparison.OrdinalIgnoreCase); + + if (unit == null || value == null) + { + return null; + } + + return new ValueUnit() + { + Unit = unit.Value(), + Value = value.Value() + }; + } + + /// + /// Convert a to an + /// + /// The value unit to convert + /// Thrown when an invalid Unit has been provided + /// An IQuantity + protected IQuantity ConvertValueUnit(ValueUnit valueUnit) + { + if (valueUnit == null || string.IsNullOrWhiteSpace(valueUnit.Unit)) + { + return null; + } + + // "MassUnit.Kilogram" => "MassUnit" and "Kilogram" + var unitEnumTypeName = valueUnit.Unit.Split('.')[0]; + var unitEnumValue = valueUnit.Unit.Split('.')[1]; + + // "UnitsNet.Units.MassUnit,UnitsNet" + var unitEnumTypeAssemblyQualifiedName = "UnitsNet.Units." + unitEnumTypeName + ",UnitsNet"; + + // -- see http://stackoverflow.com/a/6465096/1256096 for details + var unitEnumType = Type.GetType(unitEnumTypeAssemblyQualifiedName); + if (unitEnumType == null) + { + var ex = new UnitsNetException("Unable to find enum type."); + ex.Data["type"] = unitEnumTypeAssemblyQualifiedName; + throw ex; + } + + var value = valueUnit.Value; + var unitValue = (Enum)Enum.Parse(unitEnumType, unitEnumValue); // Ex: MassUnit.Kilogram + + return Quantity.From(value, unitValue); + } + + /// + /// Convert an to a + /// + /// The quantity to convert + /// + protected ValueUnit ConvertIQuantity(IQuantity quantity) + { + return new ValueUnit + { + // See ValueUnit about precision loss for quantities using decimal type. + Value = quantity.Value, + Unit = $"{quantity.QuantityInfo.UnitType.Name}.{quantity.Unit}" + }; + } + + /// + /// Create a copy of a serializer, retaining any settings but leaving out a converter to prevent loops + /// + /// The serializer to copy + /// The converter to leave out + /// A serializer with the same settings and all converters except the current one. + protected JsonSerializer CreateLocalSerializer(JsonSerializer serializer, JsonConverter currentConverter) + { + var localSerializer = new JsonSerializer() + { + Culture = serializer.Culture, + CheckAdditionalContent = serializer.CheckAdditionalContent, + Context = serializer.Context, + ContractResolver = serializer.ContractResolver, + TypeNameHandling = serializer.TypeNameHandling, + TypeNameAssemblyFormatHandling = serializer.TypeNameAssemblyFormatHandling, + Formatting = serializer.Formatting, + ConstructorHandling = serializer.ConstructorHandling, + DateFormatHandling = serializer.DateFormatHandling, + DateFormatString = serializer.DateFormatString, + DateParseHandling = serializer.DateParseHandling, + DateTimeZoneHandling = serializer.DateTimeZoneHandling, + DefaultValueHandling = serializer.DefaultValueHandling, + EqualityComparer = serializer.EqualityComparer, + FloatFormatHandling = serializer.FloatFormatHandling, + FloatParseHandling = serializer.FloatParseHandling, + MaxDepth = serializer.MaxDepth, + MetadataPropertyHandling = serializer.MetadataPropertyHandling, + MissingMemberHandling = serializer.MissingMemberHandling, + NullValueHandling = serializer.NullValueHandling, + ObjectCreationHandling = serializer.ObjectCreationHandling, + PreserveReferencesHandling = serializer.PreserveReferencesHandling, + ReferenceLoopHandling = serializer.ReferenceLoopHandling, + ReferenceResolver = serializer.ReferenceResolver, + SerializationBinder = serializer.SerializationBinder, + StringEscapeHandling = serializer.StringEscapeHandling, + TraceWriter = serializer.TraceWriter + }; + + foreach (var converter in serializer.Converters.Where(x => x != currentConverter)) + { + localSerializer.Converters.Add(converter); + } + + return localSerializer; + } + + /// + /// A structure used to serialize/deserialize Units.NET unit instances. + /// + /// + /// Quantities may use decimal, long or double as base value type and this might result + /// in a loss of precision when serializing/deserializing to decimal. + /// Decimal is the highest precision type available in .NET, but has a smaller + /// range than double. + /// + /// Json: Support decimal precision #503 + /// https://github.com/angularsen/UnitsNet/issues/503 + /// + protected sealed class ValueUnit + { + /// + /// The name of the unit + /// + /// MassUnit.Pound + /// InformationUnit.Kilobyte + public string Unit { get; [UsedImplicitly] set; } + + /// + /// The value of the unit + /// + public double Value { get; [UsedImplicitly] set; } + } + } +} diff --git a/UnitsNet.Serialization.JsonNet/UnitsNetJsonConverter.cs b/UnitsNet.Serialization.JsonNet/UnitsNetJsonConverter.cs index c8c460ccc8..217b76ea9e 100644 --- a/UnitsNet.Serialization.JsonNet/UnitsNetJsonConverter.cs +++ b/UnitsNet.Serialization.JsonNet/UnitsNetJsonConverter.cs @@ -21,6 +21,7 @@ namespace UnitsNet.Serialization.JsonNet /// * Unit enums are of type UnitsNet.Units.LengthUnit etc. /// * Unit class has a BaseUnit property returning the base unit, such as LengthUnit.Meter /// + [Obsolete("Replaced by UnitsNetIQuantityJsonConverter and UnitsNetIComparableJsonConverter (if you need support for IComparable)")] public class UnitsNetJsonConverter : JsonConverter { /// diff --git a/UnitsNet.Serialization.JsonNet/UnitsNetJsonIQuantityConverter.cs b/UnitsNet.Serialization.JsonNet/UnitsNetJsonIQuantityConverter.cs new file mode 100644 index 0000000000..cedfb93fe3 --- /dev/null +++ b/UnitsNet.Serialization.JsonNet/UnitsNetJsonIQuantityConverter.cs @@ -0,0 +1,69 @@ +// Licensed under MIT No Attribution, see LICENSE file at the root. +// Copyright 2013 Andreas Gullberg Larsen (andreas.larsen84@gmail.com). Maintained at https://github.com/angularsen/UnitsNet. + +using System; +using JetBrains.Annotations; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; + +namespace UnitsNet.Serialization.JsonNet +{ + /// + /// + /// JSON.net converter for IQuantity types (e.g. all units in UnitsNet) + /// Use this converter to serialize and deserialize UnitsNet types to and from JSON + /// + public sealed class UnitsNetJsonIQuantityConverter : UnitsNetJsonBaseConverter + { + /// + /// Writes the JSON representation of the object. + /// + /// The to write to. + /// The value to write. + /// The calling serializer. + public override void WriteJson([NotNull] JsonWriter writer, IQuantity value, [NotNull] JsonSerializer serializer) + { + writer = writer ?? throw new ArgumentNullException(nameof(writer)); + serializer = serializer ?? throw new ArgumentNullException(nameof(serializer)); + + if (value == null) + { + writer.WriteNull(); + } + + var valueUnit = ConvertIQuantity(value); + + serializer.Serialize(writer, valueUnit); + } + + /// + /// Reads the JSON representation of the object. + /// + /// The to read from. + /// Type of the object. + /// The existing value of object being read. + /// Indicates if an existing value has been provided + /// The calling serializer. + /// + /// The object value. + /// + /// Unable to parse value and unit from JSON. + public override IQuantity ReadJson([NotNull] JsonReader reader, Type objectType, IQuantity existingValue, bool hasExistingValue, + [NotNull] JsonSerializer serializer) + { + reader = reader ?? throw new ArgumentNullException(nameof(reader)); + serializer = serializer ?? throw new ArgumentNullException(nameof(serializer)); + + if (reader.TokenType == JsonToken.Null) + { + return null; + } + + var token = JToken.Load(reader); + + var valueUnit = ReadValueUnit(token); + + return ConvertValueUnit(valueUnit); + } + } +} From 62aa55495c0e95d1cff1e4c8f83856fdd63d2f9b Mon Sep 17 00:00:00 2001 From: Dirk Schuermans Date: Wed, 5 Feb 2020 14:23:57 +0100 Subject: [PATCH 2/4] Corrected naming of converters and added converter tests --- .../UnitsNetJsonBaseTest.cs | 4 +- .../UnitsNetBaseJsonConverterTest.cs | 216 ++++++++++++++++++ .../UnitsNetIComparableJsonConverterTest.cs | 123 ++++++++++ .../UnitsNetIQuantityJsonConverterTest.cs | 125 ++++++++++ ...verter.cs => UnitsNetBaseJsonConverter.cs} | 19 +- .../UnitsNetIComparableJsonConverter.cs | 4 +- ...r.cs => UnitsNetIQuantityJsonConverter.cs} | 3 +- 7 files changed, 485 insertions(+), 9 deletions(-) rename UnitsNet.Serialization.JsonNet.Tests/{ => Infrastructure}/UnitsNetJsonBaseTest.cs (88%) create mode 100644 UnitsNet.Serialization.JsonNet.Tests/UnitsNetBaseJsonConverterTest.cs create mode 100644 UnitsNet.Serialization.JsonNet.Tests/UnitsNetIComparableJsonConverterTest.cs create mode 100644 UnitsNet.Serialization.JsonNet.Tests/UnitsNetIQuantityJsonConverterTest.cs rename UnitsNet.Serialization.JsonNet/{UnitsNetJsonBaseConverter.cs => UnitsNetBaseJsonConverter.cs} (92%) rename UnitsNet.Serialization.JsonNet/{UnitsNetJsonIQuantityConverter.cs => UnitsNetIQuantityJsonConverter.cs} (95%) diff --git a/UnitsNet.Serialization.JsonNet.Tests/UnitsNetJsonBaseTest.cs b/UnitsNet.Serialization.JsonNet.Tests/Infrastructure/UnitsNetJsonBaseTest.cs similarity index 88% rename from UnitsNet.Serialization.JsonNet.Tests/UnitsNetJsonBaseTest.cs rename to UnitsNet.Serialization.JsonNet.Tests/Infrastructure/UnitsNetJsonBaseTest.cs index c2e1502924..9197ee50f7 100644 --- a/UnitsNet.Serialization.JsonNet.Tests/UnitsNetJsonBaseTest.cs +++ b/UnitsNet.Serialization.JsonNet.Tests/Infrastructure/UnitsNetJsonBaseTest.cs @@ -3,7 +3,7 @@ using Newtonsoft.Json; -namespace UnitsNet.Serialization.JsonNet.Tests +namespace UnitsNet.Serialization.JsonNet.Tests.Infrastructure { public abstract class UnitsNetJsonBaseTest { @@ -12,7 +12,7 @@ public abstract class UnitsNetJsonBaseTest protected UnitsNetJsonBaseTest() { jsonSerializerSettings = new JsonSerializerSettings {Formatting = Formatting.Indented}; - jsonSerializerSettings.Converters.Add(new UnitsNetJsonIQuantityConverter()); + jsonSerializerSettings.Converters.Add(new UnitsNetIQuantityJsonConverter()); jsonSerializerSettings.Converters.Add(new UnitsNetIComparableJsonConverter()); } diff --git a/UnitsNet.Serialization.JsonNet.Tests/UnitsNetBaseJsonConverterTest.cs b/UnitsNet.Serialization.JsonNet.Tests/UnitsNetBaseJsonConverterTest.cs new file mode 100644 index 0000000000..6431e611c5 --- /dev/null +++ b/UnitsNet.Serialization.JsonNet.Tests/UnitsNetBaseJsonConverterTest.cs @@ -0,0 +1,216 @@ +// Licensed under MIT No Attribution, see LICENSE file at the root. +// Copyright 2013 Andreas Gullberg Larsen (andreas.larsen84@gmail.com). Maintained at https://github.com/angularsen/UnitsNet. + +using System; +using System.Collections.Generic; +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; +using Newtonsoft.Json.Linq; +using Newtonsoft.Json.Serialization; +using Xunit; + +namespace UnitsNet.Serialization.JsonNet.Tests +{ + public sealed class UnitsNetBaseJsonConverterTest + { + private readonly string unitName; + private readonly double unitValue; + private readonly Power unit; + + private TestConverter sut; + + public UnitsNetBaseJsonConverterTest() + { + sut = new TestConverter(); + unitName = "PowerUnit.Watt"; + unitValue = 10.2365D; + unit = Power.FromWatts(unitValue); + } + + [Fact] + public void UnitsNetBaseJsonConverter_ConvertIQuantity_works_as_expected() + { + var result = sut.Test_ConvertIQuantity(unit); + + Assert.Equal(unitName, result.Unit); + Assert.Equal(unitValue, result.Value); + } + + [Fact] + public void UnitsNetBaseJsonConverter_ConvertIQuantity_throws_ArgumentNullException_when_quantity_is_NULL() + { + var result = Assert.Throws(() => sut.Test_ConvertIQuantity(null)); + + Assert.Equal("Value cannot be null.\r\nParameter name: quantity", result.Message); + } + + [Fact] + public void UnitsNetBaseJsonConverter_ConvertValueUnit_works_as_expected() + { + var result = sut.Test_ConvertValueUnit(unitName, unitValue); + + Assert.NotNull(result); + Assert.IsType(result); + Assert.True(unit.Equals((Power)result, 1E-5, ComparisonType.Absolute)); + + } + + [Fact] + public void UnitsNetBaseJsonConverter_ConvertValueUnit_works_with_NULL_value() + { + var result = sut.Test_ConvertValueUnit(); + + Assert.Null(result); + } + + [Fact] + public void UnitsNetBaseJsonConverter_ConvertValueUnit_throws_UnitsNetException_when_unit_does_not_exist() + { + var result = Assert.Throws(() => sut.Test_ConvertValueUnit("SomeImaginaryUnit.Watt", unitValue)); + + Assert.Equal("Unable to find enum type.", result.Message); + Assert.True(result.Data.Contains("type")); + Assert.Equal("UnitsNet.Units.SomeImaginaryUnit,UnitsNet", result.Data["type"]); + } + + [Fact] + public void UnitsNetBaseJsonConverter_ConvertValueUnit_throws_UnitsNetException_when_unit_is_in_unexpected_format() + { + var result = Assert.Throws(() => sut.Test_ConvertValueUnit("PowerUnit Watt", unitValue)); + + Assert.Equal("\"PowerUnit Watt\" is not a valid unit.", result.Message); + Assert.True(result.Data.Contains("type")); + Assert.Equal("PowerUnit Watt", result.Data["type"]); + } + + [Fact] + public void UnitsNetBaseJsonConverter_CreateLocalSerializer_works_as_expected() + { + //Possible improvement: Set all possible settings and test each one. But the main goal of CreateLocalSerializer is that the current serializer is left out. + var serializer = JsonSerializer.Create(new JsonSerializerSettings() + { + TypeNameHandling = TypeNameHandling.Arrays, + Converters = new List() + { + + new BinaryConverter(), + sut, + new DataTableConverter() + }, + ContractResolver = new CamelCasePropertyNamesContractResolver() + }); + + var result = sut.Test_CreateLocalSerializer(serializer); + + Assert.Equal(TypeNameHandling.Arrays, result.TypeNameHandling); + Assert.Equal(2, result.Converters.Count); + Assert.Collection(result.Converters, + (converter) => Assert.IsType(converter), + (converter) => Assert.IsType(converter)); + Assert.IsType(result.ContractResolver); + } + + [Fact] + public void UnitsNetBaseJsonConverter_ReadValueUnit_work_as_expected() + { + var token = new JObject(); + + token.Add("Unit", unitName); + token.Add("Value", unitValue); + + var result = sut.Test_ReadValueUnit(token); + + Assert.NotNull(result); + Assert.Equal(unitName, result?.Unit); + Assert.Equal(unitValue, result?.Value); + } + + [Fact] + public void UnitsNetBaseJsonConverter_ReadValueUnit_works_with_empty_token() + { + var token = new JObject(); + + var result = sut.Test_ReadValueUnit(token); + + Assert.Null(result); + } + + [Theory] + [InlineData(false, true)] + [InlineData(true, false)] + public void UnitsNetBaseJsonConverter_ReadValueUnit_returns_null_when_unit_or_value_is_missing(bool withUnit, bool withValue) + { + var token = new JObject(); + + if (withUnit) + { + token.Add("Unit", unitName); + } + + if (withValue) + { + token.Add("Value", unitValue); + } + + var result = sut.Test_ReadValueUnit(token); + + Assert.Null(result); + } + + [Theory] + [InlineData("Unit", "Value")] + [InlineData("unit", "Value")] + [InlineData("Unit", "value")] + [InlineData("unit", "value")] + [InlineData("unIT", "vAlUe")] + public void UnitsNetBaseJsonConverter_ReadValueUnit_works_case_insensitive(string unitPropertyName, string valuePropertyName) + { + var token = new JObject(); + + token.Add(unitPropertyName, unitName); + token.Add(valuePropertyName, unitValue); + + var result = sut.Test_ReadValueUnit(token); + + Assert.NotNull(result); + Assert.Equal(unitName, result?.Unit); + Assert.Equal(unitValue, result?.Value); + } + + /// + /// Dummy converter, used to access protected methods on abstract UnitsNetBaseJsonConverter{T} + /// + private class TestConverter : UnitsNetBaseJsonConverter + { + public override bool CanRead => false; + public override bool CanWrite => false; + public override void WriteJson(JsonWriter writer, string value, JsonSerializer serializer) => throw new NotImplementedException(); + public override string ReadJson(JsonReader reader, Type objectType, string existingValue, bool hasExistingValue, JsonSerializer serializer) => throw new NotImplementedException(); + + public (string Unit, double Value) Test_ConvertIQuantity(IQuantity value) + { + var result = ConvertIQuantity(value); + + return (result.Unit, result.Value); + } + + public IQuantity Test_ConvertValueUnit(string unit, double value) => Test_ConvertValueUnit(new ValueUnit() {Unit = unit, Value = value}); + public IQuantity Test_ConvertValueUnit() => Test_ConvertValueUnit(null); + private IQuantity Test_ConvertValueUnit(ValueUnit valueUnit) => ConvertValueUnit(valueUnit); + + public JsonSerializer Test_CreateLocalSerializer(JsonSerializer serializer) => CreateLocalSerializer(serializer, this); + + public (string Unit, double Value)? Test_ReadValueUnit(JToken jsonToken) + { + var result = ReadValueUnit(jsonToken); + + if (result == null) + { + return null; + } + + return (result.Unit, result.Value); + } + } + } +} diff --git a/UnitsNet.Serialization.JsonNet.Tests/UnitsNetIComparableJsonConverterTest.cs b/UnitsNet.Serialization.JsonNet.Tests/UnitsNetIComparableJsonConverterTest.cs new file mode 100644 index 0000000000..625c3f7c44 --- /dev/null +++ b/UnitsNet.Serialization.JsonNet.Tests/UnitsNetIComparableJsonConverterTest.cs @@ -0,0 +1,123 @@ +// Licensed under MIT No Attribution, see LICENSE file at the root. +// Copyright 2013 Andreas Gullberg Larsen (andreas.larsen84@gmail.com). Maintained at https://github.com/angularsen/UnitsNet. + +using System; +using System.Collections.Generic; +using System.IO; +using Newtonsoft.Json; +using UnitsNet.Serialization.JsonNet.Tests.Infrastructure; +using Xunit; + +namespace UnitsNet.Serialization.JsonNet.Tests +{ + public sealed class UnitsNetIComparableJsonConverterTest + { + private UnitsNetIComparableJsonConverter sut; + + public UnitsNetIComparableJsonConverterTest() + { + sut = new UnitsNetIComparableJsonConverter(); + } + + [Fact] + public void UnitsNetIComparableJsonConverter_CanWrite_returns_false() + { + Assert.False(sut.CanWrite); + } + + [Fact] + public void UnitsNetIComparableJsonConverter_WriteJson_throws_NotImplementedException() + { + Assert.Throws(() => sut.WriteJson(null, null, null)); + } + + [Fact] + public void UnitsNetIComparableJsonConverter_CanRead_returns_true() + { + Assert.True(sut.CanRead); + } + + public static IEnumerable ReadJson_NullArguments => new[] + { + new object[] { null, JsonSerializer.CreateDefault(), "reader" }, + new object[] { new JsonTextReader(new StringReader("")), null, "serializer" } + }; + + [Theory] + [MemberData(nameof(ReadJson_NullArguments))] + public void UnitsNetIComparableJsonConverter_ReadJson_throws_ArgumentNullException_when_arguments_are_null(JsonReader reader, JsonSerializer serializer, string paramName) + { + var exception = Assert.Throws(() => sut.ReadJson(reader, typeof(IQuantity), null, false, serializer)); + + Assert.Equal($"Value cannot be null.\r\nParameter name: {paramName}", exception.Message); + } + + [Fact] + public void UnitsNetIComparableJsonConverter_ReadJson_handles_NULL_values_correctly() + { + var json = "null"; + + using(var stringReader = new StringReader(json)) + using (var jsonReader = new JsonTextReader(stringReader)) + { + var result = sut.ReadJson(jsonReader, typeof(IQuantity), null, false, JsonSerializer.CreateDefault()); + + Assert.Null(result); + } + } + + [Fact] + public void UnitsNetIComparableJsonConverter_ReadJson_deserializes_as_concrete_type_when_objectType_not_IComparable() + { + string json = "{ \"value\": 12345 }"; + + IComparable result; + + using (var stringReader = new StringReader(json)) + using(var jsonReader = new JsonTextReader(stringReader)) + { + result = sut.ReadJson(jsonReader, typeof(TestObjectWithValueAsIComparable), null, false, JsonSerializer.CreateDefault()); + } + + Assert.NotNull(result); + Assert.IsType(result); + Assert.Equal(12345, ((TestObjectWithValueAsIComparable)result).Value); + } + + [Fact] + public void UnitsNetIComparableJsonConverter_ReadJson_falls_back_to_default_IComparable_deserialization_when_unable_to_deserialize_as_ValueUnit() + { + string json = "{ \"$type\": \"UnitsNet.Serialization.JsonNet.Tests.Infrastructure.TestObjectWithValueAsIComparable, UnitsNet.Serialization.JsonNet.Tests\", \"value\": 120 }"; + IComparable result; + + using (var stringReader = new StringReader(json)) + using(var jsonReader = new JsonTextReader(stringReader)) + { + var serializer = JsonSerializer.Create(new JsonSerializerSettings() {TypeNameHandling = TypeNameHandling.Auto}); + result = sut.ReadJson(jsonReader, typeof(IComparable), null, false, serializer); + } + + Assert.NotNull(result); + Assert.IsType(result); + Assert.Equal(120, ((TestObjectWithValueAsIComparable)result).Value); + } + + [Fact] + public void UnitsNetIComparableJsonConverter_ReadJson_works_as_expected() + { + string json = "{ \"value\": 120, \"unit\": \"PowerUnit.Watt\" }"; + IComparable result; + + using (var stringReader = new StringReader(json)) + using(var jsonReader = new JsonTextReader(stringReader)) + { + var serializer = JsonSerializer.Create(new JsonSerializerSettings() {TypeNameHandling = TypeNameHandling.Auto}); + result = sut.ReadJson(jsonReader, typeof(IComparable), null, false, serializer); + } + + Assert.NotNull(result); + Assert.IsType(result); + Assert.Equal(120D, ((Power)result).Watts); + } + } +} diff --git a/UnitsNet.Serialization.JsonNet.Tests/UnitsNetIQuantityJsonConverterTest.cs b/UnitsNet.Serialization.JsonNet.Tests/UnitsNetIQuantityJsonConverterTest.cs new file mode 100644 index 0000000000..f789f60a2e --- /dev/null +++ b/UnitsNet.Serialization.JsonNet.Tests/UnitsNetIQuantityJsonConverterTest.cs @@ -0,0 +1,125 @@ +// Licensed under MIT No Attribution, see LICENSE file at the root. +// Copyright 2013 Andreas Gullberg Larsen (andreas.larsen84@gmail.com). Maintained at https://github.com/angularsen/UnitsNet. + +using System; +using System.Collections; +using System.Collections.Generic; +using System.IO; +using System.Text; +using Newtonsoft.Json; +using Xunit; + +namespace UnitsNet.Serialization.JsonNet.Tests +{ + public sealed class UnitsNetIQuantityJsonConverterTest + { + private UnitsNetIQuantityJsonConverter sut; + + public UnitsNetIQuantityJsonConverterTest() + { + sut = new UnitsNetIQuantityJsonConverter(); + } + + [Fact] + public void UnitsNetIQuantityJsonConverter_CanWrite_returns_true() + { + Assert.True(sut.CanWrite); + } + + public static IEnumerable WriteJson_NullArguments => new [] + { + new object[] { null, new JsonSerializer(), "writer"}, + new object[] { new JsonTextWriter(new StringWriter()), null, "serializer"}, + }; + + [Theory] + [MemberData(nameof(WriteJson_NullArguments))] + public void + UnitsNetIQuantityJsonConverter_WriteJson_throws_ArgumentNullException_when_arguments_are_null(JsonWriter writer, JsonSerializer serializer, string parameterName) + { + var exception = Assert.Throws(() => sut.WriteJson(writer, Power.FromWatts(10D), serializer)); + + Assert.Equal($"Value cannot be null.\r\nParameter name: {parameterName}", exception.Message); + } + + [Fact] + public void UnitsNetIQuantityJsonConverter_WriteJson_works_with_NULL_value() + { + var result = new StringBuilder(); + using(var stringWriter = new StringWriter(result)) + using (var writer = new JsonTextWriter(stringWriter)) + { + sut.WriteJson(writer, null, JsonSerializer.CreateDefault()); + } + + Assert.Equal("null", result.ToString()); + } + + [Fact] + public void UnitsNetIQuantityJsonConverter_WriteJson_works_as_expected() + { + var result = new StringBuilder(); + + using (var stringWriter = new StringWriter(result)) + using(var writer = new JsonTextWriter(stringWriter)) + { + sut.WriteJson(writer, Power.FromWatts(10.2365D), JsonSerializer.CreateDefault()); + } + + Assert.Equal("{\"Unit\":\"PowerUnit.Watt\",\"Value\":10.2365}", result.ToString()); + } + + [Fact] + public void UnitsNetIQuantityJsonConverter_CanRead_returns_true() + { + Assert.True(sut.CanRead); + } + + public static IEnumerable ReadJson_NullArguments => new[] + { + new object[] { null, JsonSerializer.CreateDefault(), "reader" }, + new object[] { new JsonTextReader(new StringReader("")), null, "serializer" } + }; + + [Theory] + [MemberData(nameof(ReadJson_NullArguments))] + public void UnitsNetIQuantityJsonConverter_ReadJson_throws_ArgumentNullException_when_arguments_are_null(JsonReader reader, JsonSerializer serializer, string paramName) + { + var exception = Assert.Throws(() => sut.ReadJson(reader, typeof(IQuantity), null, false, serializer)); + + Assert.Equal($"Value cannot be null.\r\nParameter name: {paramName}", exception.Message); + } + + [Fact] + public void UnitsNetIQuantityJsonConverter_ReadJson_handles_NULL_values_correctly() + { + var json = "null"; + + using(var stringReader = new StringReader(json)) + using (var jsonReader = new JsonTextReader(stringReader)) + { + var result = sut.ReadJson(jsonReader, typeof(IQuantity), null, false, JsonSerializer.CreateDefault()); + + Assert.Null(result); + } + } + + [Fact] + public void UnitsNetIQuantityJsonConverter_ReadJson_works_as_expected() + { + var json = "{ \"unit\": \"PowerUnit.Watt\", \"Value\": 10.3654}"; + + IQuantity result; + + using(var stringReader = new StringReader(json)) + using (var jsonReader = new JsonTextReader(stringReader)) + { + result = sut.ReadJson(jsonReader, typeof(IQuantity), null, false, JsonSerializer.CreateDefault()); + } + + Assert.NotNull(result); + Assert.IsType(result); + Assert.Equal(10.3654D, ((Power)result).Watts); + } + } +} diff --git a/UnitsNet.Serialization.JsonNet/UnitsNetJsonBaseConverter.cs b/UnitsNet.Serialization.JsonNet/UnitsNetBaseJsonConverter.cs similarity index 92% rename from UnitsNet.Serialization.JsonNet/UnitsNetJsonBaseConverter.cs rename to UnitsNet.Serialization.JsonNet/UnitsNetBaseJsonConverter.cs index 1d6faecdc1..d2916dadb9 100644 --- a/UnitsNet.Serialization.JsonNet/UnitsNetJsonBaseConverter.cs +++ b/UnitsNet.Serialization.JsonNet/UnitsNetBaseJsonConverter.cs @@ -11,10 +11,10 @@ namespace UnitsNet.Serialization.JsonNet { /// /// Base converter for serializing and deserializing UnitsNet types to and from JSON. - /// Contains shared functionality used by and + /// Contains shared functionality used by and /// /// - public abstract class UnitsNetJsonBaseConverter : JsonConverter + public abstract class UnitsNetBaseJsonConverter : JsonConverter { /// /// Reads the "Unit" and "Value" properties from a JSON string @@ -58,9 +58,18 @@ protected IQuantity ConvertValueUnit(ValueUnit valueUnit) return null; } + var unitParts = valueUnit.Unit.Split('.'); + + if (unitParts.Length != 2) + { + var ex = new UnitsNetException($"\"{valueUnit.Unit}\" is not a valid unit."); + ex.Data["type"] = valueUnit.Unit; + throw ex; + } + // "MassUnit.Kilogram" => "MassUnit" and "Kilogram" - var unitEnumTypeName = valueUnit.Unit.Split('.')[0]; - var unitEnumValue = valueUnit.Unit.Split('.')[1]; + var unitEnumTypeName = unitParts[0]; + var unitEnumValue = unitParts[1]; // "UnitsNet.Units.MassUnit,UnitsNet" var unitEnumTypeAssemblyQualifiedName = "UnitsNet.Units." + unitEnumTypeName + ",UnitsNet"; @@ -87,6 +96,8 @@ protected IQuantity ConvertValueUnit(ValueUnit valueUnit) /// protected ValueUnit ConvertIQuantity(IQuantity quantity) { + quantity = quantity ?? throw new ArgumentNullException(nameof(quantity)); + return new ValueUnit { // See ValueUnit about precision loss for quantities using decimal type. diff --git a/UnitsNet.Serialization.JsonNet/UnitsNetIComparableJsonConverter.cs b/UnitsNet.Serialization.JsonNet/UnitsNetIComparableJsonConverter.cs index 8550c20a98..dfd7e391a9 100644 --- a/UnitsNet.Serialization.JsonNet/UnitsNetIComparableJsonConverter.cs +++ b/UnitsNet.Serialization.JsonNet/UnitsNetIComparableJsonConverter.cs @@ -12,11 +12,11 @@ namespace UnitsNet.Serialization.JsonNet /// /// JSON.Net converter for IComparable properties that are actually UnitsNet units. /// Use with caution, as this might interfere with any other converters for IComparable. - /// Must be registered in the collection -after- + /// Must be registered in the collection -after- /// Should only be used when UnitsNet types are assigned to properties of type IComparable. /// Requires TypeNameHandling on to be set to something other than so that it outputs $type when serializing. /// - public sealed class UnitsNetIComparableJsonConverter : UnitsNetJsonBaseConverter + public sealed class UnitsNetIComparableJsonConverter : UnitsNetBaseJsonConverter { /// /// Indicates if this JsonConverter is capable of writing JSON diff --git a/UnitsNet.Serialization.JsonNet/UnitsNetJsonIQuantityConverter.cs b/UnitsNet.Serialization.JsonNet/UnitsNetIQuantityJsonConverter.cs similarity index 95% rename from UnitsNet.Serialization.JsonNet/UnitsNetJsonIQuantityConverter.cs rename to UnitsNet.Serialization.JsonNet/UnitsNetIQuantityJsonConverter.cs index cedfb93fe3..4f258040f3 100644 --- a/UnitsNet.Serialization.JsonNet/UnitsNetJsonIQuantityConverter.cs +++ b/UnitsNet.Serialization.JsonNet/UnitsNetIQuantityJsonConverter.cs @@ -13,7 +13,7 @@ namespace UnitsNet.Serialization.JsonNet /// JSON.net converter for IQuantity types (e.g. all units in UnitsNet) /// Use this converter to serialize and deserialize UnitsNet types to and from JSON /// - public sealed class UnitsNetJsonIQuantityConverter : UnitsNetJsonBaseConverter + public sealed class UnitsNetIQuantityJsonConverter : UnitsNetBaseJsonConverter { /// /// Writes the JSON representation of the object. @@ -29,6 +29,7 @@ public override void WriteJson([NotNull] JsonWriter writer, IQuantity value, [No if (value == null) { writer.WriteNull(); + return; } var valueUnit = ConvertIQuantity(value); From 9067ba54a18b8073d7ba3c3d839d97472b6132cf Mon Sep 17 00:00:00 2001 From: Andreas Gullberg Larsen Date: Thu, 6 Feb 2020 23:43:14 +0100 Subject: [PATCH 3/4] Rename field --- .../Infrastructure/UnitsNetJsonBaseTest.cs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/UnitsNet.Serialization.JsonNet.Tests/Infrastructure/UnitsNetJsonBaseTest.cs b/UnitsNet.Serialization.JsonNet.Tests/Infrastructure/UnitsNetJsonBaseTest.cs index 9197ee50f7..d961281e30 100644 --- a/UnitsNet.Serialization.JsonNet.Tests/Infrastructure/UnitsNetJsonBaseTest.cs +++ b/UnitsNet.Serialization.JsonNet.Tests/Infrastructure/UnitsNetJsonBaseTest.cs @@ -7,27 +7,27 @@ namespace UnitsNet.Serialization.JsonNet.Tests.Infrastructure { public abstract class UnitsNetJsonBaseTest { - private readonly JsonSerializerSettings jsonSerializerSettings; + private readonly JsonSerializerSettings _jsonSerializerSettings; protected UnitsNetJsonBaseTest() { - jsonSerializerSettings = new JsonSerializerSettings {Formatting = Formatting.Indented}; - jsonSerializerSettings.Converters.Add(new UnitsNetIQuantityJsonConverter()); - jsonSerializerSettings.Converters.Add(new UnitsNetIComparableJsonConverter()); + _jsonSerializerSettings = new JsonSerializerSettings {Formatting = Formatting.Indented}; + _jsonSerializerSettings.Converters.Add(new UnitsNetIQuantityJsonConverter()); + _jsonSerializerSettings.Converters.Add(new UnitsNetIComparableJsonConverter()); } protected string SerializeObject(object obj, TypeNameHandling typeNameHandling = TypeNameHandling.None) { - jsonSerializerSettings.TypeNameHandling = typeNameHandling; + _jsonSerializerSettings.TypeNameHandling = typeNameHandling; - return JsonConvert.SerializeObject(obj, jsonSerializerSettings).Replace("\r\n", "\n"); + return JsonConvert.SerializeObject(obj, _jsonSerializerSettings).Replace("\r\n", "\n"); } protected T DeserializeObject(string json, TypeNameHandling typeNameHandling = TypeNameHandling.None) { - jsonSerializerSettings.TypeNameHandling = typeNameHandling; + _jsonSerializerSettings.TypeNameHandling = typeNameHandling; - return JsonConvert.DeserializeObject(json, jsonSerializerSettings); + return JsonConvert.DeserializeObject(json, _jsonSerializerSettings); } } } From 9e18710bb22f70c79b7fade44eb8726392af6640 Mon Sep 17 00:00:00 2001 From: Dirk Schuermans Date: Fri, 7 Feb 2020 10:12:14 +0100 Subject: [PATCH 4/4] Resolved PR remarks --- .../UnitsNetBaseJsonConverterTest.cs | 61 ++- .../UnitsNetIComparableJsonConverterTest.cs | 20 +- .../UnitsNetIQuantityJsonConverterTest.cs | 20 +- .../UnitsNetJsonDeserializationTests.cs | 348 +++++++++--------- .../UnitsNetJsonSerializationTests.cs | 202 +++++----- .../UnitsNetBaseJsonConverter.cs | 2 +- .../UnitsNetIComparableJsonConverter.cs | 6 +- 7 files changed, 328 insertions(+), 331 deletions(-) diff --git a/UnitsNet.Serialization.JsonNet.Tests/UnitsNetBaseJsonConverterTest.cs b/UnitsNet.Serialization.JsonNet.Tests/UnitsNetBaseJsonConverterTest.cs index 6431e611c5..eeaaaf0c5e 100644 --- a/UnitsNet.Serialization.JsonNet.Tests/UnitsNetBaseJsonConverterTest.cs +++ b/UnitsNet.Serialization.JsonNet.Tests/UnitsNetBaseJsonConverterTest.cs @@ -13,33 +13,26 @@ namespace UnitsNet.Serialization.JsonNet.Tests { public sealed class UnitsNetBaseJsonConverterTest { - private readonly string unitName; - private readonly double unitValue; - private readonly Power unit; - - private TestConverter sut; + private TestConverter _sut; public UnitsNetBaseJsonConverterTest() { - sut = new TestConverter(); - unitName = "PowerUnit.Watt"; - unitValue = 10.2365D; - unit = Power.FromWatts(unitValue); + _sut = new TestConverter(); } [Fact] public void UnitsNetBaseJsonConverter_ConvertIQuantity_works_as_expected() { - var result = sut.Test_ConvertIQuantity(unit); + var result = _sut.Test_ConvertIQuantity(Power.FromWatts(10.2365D)); - Assert.Equal(unitName, result.Unit); - Assert.Equal(unitValue, result.Value); + Assert.Equal("PowerUnit.Watt", result.Unit); + Assert.Equal(10.2365D, result.Value); } [Fact] public void UnitsNetBaseJsonConverter_ConvertIQuantity_throws_ArgumentNullException_when_quantity_is_NULL() { - var result = Assert.Throws(() => sut.Test_ConvertIQuantity(null)); + var result = Assert.Throws(() => _sut.Test_ConvertIQuantity(null)); Assert.Equal("Value cannot be null.\r\nParameter name: quantity", result.Message); } @@ -47,18 +40,18 @@ public void UnitsNetBaseJsonConverter_ConvertIQuantity_throws_ArgumentNullExcept [Fact] public void UnitsNetBaseJsonConverter_ConvertValueUnit_works_as_expected() { - var result = sut.Test_ConvertValueUnit(unitName, unitValue); + var result = _sut.Test_ConvertValueUnit("PowerUnit.Watt", 10.2365D); Assert.NotNull(result); Assert.IsType(result); - Assert.True(unit.Equals((Power)result, 1E-5, ComparisonType.Absolute)); + Assert.True(Power.FromWatts(10.2365D).Equals((Power)result, 1E-5, ComparisonType.Absolute)); } [Fact] public void UnitsNetBaseJsonConverter_ConvertValueUnit_works_with_NULL_value() { - var result = sut.Test_ConvertValueUnit(); + var result = _sut.Test_ConvertValueUnit(); Assert.Null(result); } @@ -66,7 +59,7 @@ public void UnitsNetBaseJsonConverter_ConvertValueUnit_works_with_NULL_value() [Fact] public void UnitsNetBaseJsonConverter_ConvertValueUnit_throws_UnitsNetException_when_unit_does_not_exist() { - var result = Assert.Throws(() => sut.Test_ConvertValueUnit("SomeImaginaryUnit.Watt", unitValue)); + var result = Assert.Throws(() => _sut.Test_ConvertValueUnit("SomeImaginaryUnit.Watt", 10.2365D)); Assert.Equal("Unable to find enum type.", result.Message); Assert.True(result.Data.Contains("type")); @@ -76,7 +69,7 @@ public void UnitsNetBaseJsonConverter_ConvertValueUnit_throws_UnitsNetException_ [Fact] public void UnitsNetBaseJsonConverter_ConvertValueUnit_throws_UnitsNetException_when_unit_is_in_unexpected_format() { - var result = Assert.Throws(() => sut.Test_ConvertValueUnit("PowerUnit Watt", unitValue)); + var result = Assert.Throws(() => _sut.Test_ConvertValueUnit("PowerUnit Watt", 10.2365D)); Assert.Equal("\"PowerUnit Watt\" is not a valid unit.", result.Message); Assert.True(result.Data.Contains("type")); @@ -94,13 +87,13 @@ public void UnitsNetBaseJsonConverter_CreateLocalSerializer_works_as_expected() { new BinaryConverter(), - sut, + _sut, new DataTableConverter() }, ContractResolver = new CamelCasePropertyNamesContractResolver() }); - var result = sut.Test_CreateLocalSerializer(serializer); + var result = _sut.Test_CreateLocalSerializer(serializer); Assert.Equal(TypeNameHandling.Arrays, result.TypeNameHandling); Assert.Equal(2, result.Converters.Count); @@ -115,14 +108,14 @@ public void UnitsNetBaseJsonConverter_ReadValueUnit_work_as_expected() { var token = new JObject(); - token.Add("Unit", unitName); - token.Add("Value", unitValue); + token.Add("Unit", "PowerUnit.Watt"); + token.Add("Value", 10.2365D); - var result = sut.Test_ReadValueUnit(token); + var result = _sut.Test_ReadValueUnit(token); Assert.NotNull(result); - Assert.Equal(unitName, result?.Unit); - Assert.Equal(unitValue, result?.Value); + Assert.Equal("PowerUnit.Watt", result?.Unit); + Assert.Equal(10.2365D, result?.Value); } [Fact] @@ -130,7 +123,7 @@ public void UnitsNetBaseJsonConverter_ReadValueUnit_works_with_empty_token() { var token = new JObject(); - var result = sut.Test_ReadValueUnit(token); + var result = _sut.Test_ReadValueUnit(token); Assert.Null(result); } @@ -144,15 +137,15 @@ public void UnitsNetBaseJsonConverter_ReadValueUnit_returns_null_when_unit_or_va if (withUnit) { - token.Add("Unit", unitName); + token.Add("Unit", "PowerUnit.Watt"); } if (withValue) { - token.Add("Value", unitValue); + token.Add("Value", 10.2365D); } - var result = sut.Test_ReadValueUnit(token); + var result = _sut.Test_ReadValueUnit(token); Assert.Null(result); } @@ -167,14 +160,14 @@ public void UnitsNetBaseJsonConverter_ReadValueUnit_works_case_insensitive(strin { var token = new JObject(); - token.Add(unitPropertyName, unitName); - token.Add(valuePropertyName, unitValue); + token.Add(unitPropertyName, "PowerUnit.Watt"); + token.Add(valuePropertyName, 10.2365D); - var result = sut.Test_ReadValueUnit(token); + var result = _sut.Test_ReadValueUnit(token); Assert.NotNull(result); - Assert.Equal(unitName, result?.Unit); - Assert.Equal(unitValue, result?.Value); + Assert.Equal("PowerUnit.Watt", result?.Unit); + Assert.Equal(10.2365D, result?.Value); } /// diff --git a/UnitsNet.Serialization.JsonNet.Tests/UnitsNetIComparableJsonConverterTest.cs b/UnitsNet.Serialization.JsonNet.Tests/UnitsNetIComparableJsonConverterTest.cs index 625c3f7c44..31662f6c22 100644 --- a/UnitsNet.Serialization.JsonNet.Tests/UnitsNetIComparableJsonConverterTest.cs +++ b/UnitsNet.Serialization.JsonNet.Tests/UnitsNetIComparableJsonConverterTest.cs @@ -12,29 +12,29 @@ namespace UnitsNet.Serialization.JsonNet.Tests { public sealed class UnitsNetIComparableJsonConverterTest { - private UnitsNetIComparableJsonConverter sut; + private UnitsNetIComparableJsonConverter _sut; public UnitsNetIComparableJsonConverterTest() { - sut = new UnitsNetIComparableJsonConverter(); + _sut = new UnitsNetIComparableJsonConverter(); } [Fact] public void UnitsNetIComparableJsonConverter_CanWrite_returns_false() { - Assert.False(sut.CanWrite); + Assert.False(_sut.CanWrite); } [Fact] public void UnitsNetIComparableJsonConverter_WriteJson_throws_NotImplementedException() { - Assert.Throws(() => sut.WriteJson(null, null, null)); + Assert.Throws(() => _sut.WriteJson(null, null, null)); } [Fact] public void UnitsNetIComparableJsonConverter_CanRead_returns_true() { - Assert.True(sut.CanRead); + Assert.True(_sut.CanRead); } public static IEnumerable ReadJson_NullArguments => new[] @@ -47,7 +47,7 @@ public void UnitsNetIComparableJsonConverter_CanRead_returns_true() [MemberData(nameof(ReadJson_NullArguments))] public void UnitsNetIComparableJsonConverter_ReadJson_throws_ArgumentNullException_when_arguments_are_null(JsonReader reader, JsonSerializer serializer, string paramName) { - var exception = Assert.Throws(() => sut.ReadJson(reader, typeof(IQuantity), null, false, serializer)); + var exception = Assert.Throws(() => _sut.ReadJson(reader, typeof(IQuantity), null, false, serializer)); Assert.Equal($"Value cannot be null.\r\nParameter name: {paramName}", exception.Message); } @@ -60,7 +60,7 @@ public void UnitsNetIComparableJsonConverter_ReadJson_handles_NULL_values_correc using(var stringReader = new StringReader(json)) using (var jsonReader = new JsonTextReader(stringReader)) { - var result = sut.ReadJson(jsonReader, typeof(IQuantity), null, false, JsonSerializer.CreateDefault()); + var result = _sut.ReadJson(jsonReader, typeof(IQuantity), null, false, JsonSerializer.CreateDefault()); Assert.Null(result); } @@ -76,7 +76,7 @@ public void UnitsNetIComparableJsonConverter_ReadJson_deserializes_as_concrete_t using (var stringReader = new StringReader(json)) using(var jsonReader = new JsonTextReader(stringReader)) { - result = sut.ReadJson(jsonReader, typeof(TestObjectWithValueAsIComparable), null, false, JsonSerializer.CreateDefault()); + result = _sut.ReadJson(jsonReader, typeof(TestObjectWithValueAsIComparable), null, false, JsonSerializer.CreateDefault()); } Assert.NotNull(result); @@ -94,7 +94,7 @@ public void UnitsNetIComparableJsonConverter_ReadJson_falls_back_to_default_ICom using(var jsonReader = new JsonTextReader(stringReader)) { var serializer = JsonSerializer.Create(new JsonSerializerSettings() {TypeNameHandling = TypeNameHandling.Auto}); - result = sut.ReadJson(jsonReader, typeof(IComparable), null, false, serializer); + result = _sut.ReadJson(jsonReader, typeof(IComparable), null, false, serializer); } Assert.NotNull(result); @@ -112,7 +112,7 @@ public void UnitsNetIComparableJsonConverter_ReadJson_works_as_expected() using(var jsonReader = new JsonTextReader(stringReader)) { var serializer = JsonSerializer.Create(new JsonSerializerSettings() {TypeNameHandling = TypeNameHandling.Auto}); - result = sut.ReadJson(jsonReader, typeof(IComparable), null, false, serializer); + result = _sut.ReadJson(jsonReader, typeof(IComparable), null, false, serializer); } Assert.NotNull(result); diff --git a/UnitsNet.Serialization.JsonNet.Tests/UnitsNetIQuantityJsonConverterTest.cs b/UnitsNet.Serialization.JsonNet.Tests/UnitsNetIQuantityJsonConverterTest.cs index f789f60a2e..429466036c 100644 --- a/UnitsNet.Serialization.JsonNet.Tests/UnitsNetIQuantityJsonConverterTest.cs +++ b/UnitsNet.Serialization.JsonNet.Tests/UnitsNetIQuantityJsonConverterTest.cs @@ -13,17 +13,17 @@ namespace UnitsNet.Serialization.JsonNet.Tests { public sealed class UnitsNetIQuantityJsonConverterTest { - private UnitsNetIQuantityJsonConverter sut; + private UnitsNetIQuantityJsonConverter _sut; public UnitsNetIQuantityJsonConverterTest() { - sut = new UnitsNetIQuantityJsonConverter(); + _sut = new UnitsNetIQuantityJsonConverter(); } [Fact] public void UnitsNetIQuantityJsonConverter_CanWrite_returns_true() { - Assert.True(sut.CanWrite); + Assert.True(_sut.CanWrite); } public static IEnumerable WriteJson_NullArguments => new [] @@ -37,7 +37,7 @@ public void UnitsNetIQuantityJsonConverter_CanWrite_returns_true() public void UnitsNetIQuantityJsonConverter_WriteJson_throws_ArgumentNullException_when_arguments_are_null(JsonWriter writer, JsonSerializer serializer, string parameterName) { - var exception = Assert.Throws(() => sut.WriteJson(writer, Power.FromWatts(10D), serializer)); + var exception = Assert.Throws(() => _sut.WriteJson(writer, Power.FromWatts(10D), serializer)); Assert.Equal($"Value cannot be null.\r\nParameter name: {parameterName}", exception.Message); } @@ -49,7 +49,7 @@ public void UnitsNetIQuantityJsonConverter_WriteJson_works_with_NULL_value() using(var stringWriter = new StringWriter(result)) using (var writer = new JsonTextWriter(stringWriter)) { - sut.WriteJson(writer, null, JsonSerializer.CreateDefault()); + _sut.WriteJson(writer, null, JsonSerializer.CreateDefault()); } Assert.Equal("null", result.ToString()); @@ -63,7 +63,7 @@ public void UnitsNetIQuantityJsonConverter_WriteJson_works_as_expected() using (var stringWriter = new StringWriter(result)) using(var writer = new JsonTextWriter(stringWriter)) { - sut.WriteJson(writer, Power.FromWatts(10.2365D), JsonSerializer.CreateDefault()); + _sut.WriteJson(writer, Power.FromWatts(10.2365D), JsonSerializer.CreateDefault()); } Assert.Equal("{\"Unit\":\"PowerUnit.Watt\",\"Value\":10.2365}", result.ToString()); @@ -72,7 +72,7 @@ public void UnitsNetIQuantityJsonConverter_WriteJson_works_as_expected() [Fact] public void UnitsNetIQuantityJsonConverter_CanRead_returns_true() { - Assert.True(sut.CanRead); + Assert.True(_sut.CanRead); } public static IEnumerable ReadJson_NullArguments => new[] @@ -85,7 +85,7 @@ public void UnitsNetIQuantityJsonConverter_CanRead_returns_true() [MemberData(nameof(ReadJson_NullArguments))] public void UnitsNetIQuantityJsonConverter_ReadJson_throws_ArgumentNullException_when_arguments_are_null(JsonReader reader, JsonSerializer serializer, string paramName) { - var exception = Assert.Throws(() => sut.ReadJson(reader, typeof(IQuantity), null, false, serializer)); + var exception = Assert.Throws(() => _sut.ReadJson(reader, typeof(IQuantity), null, false, serializer)); Assert.Equal($"Value cannot be null.\r\nParameter name: {paramName}", exception.Message); } @@ -98,7 +98,7 @@ public void UnitsNetIQuantityJsonConverter_ReadJson_handles_NULL_values_correctl using(var stringReader = new StringReader(json)) using (var jsonReader = new JsonTextReader(stringReader)) { - var result = sut.ReadJson(jsonReader, typeof(IQuantity), null, false, JsonSerializer.CreateDefault()); + var result = _sut.ReadJson(jsonReader, typeof(IQuantity), null, false, JsonSerializer.CreateDefault()); Assert.Null(result); } @@ -114,7 +114,7 @@ public void UnitsNetIQuantityJsonConverter_ReadJson_works_as_expected() using(var stringReader = new StringReader(json)) using (var jsonReader = new JsonTextReader(stringReader)) { - result = sut.ReadJson(jsonReader, typeof(IQuantity), null, false, JsonSerializer.CreateDefault()); + result = _sut.ReadJson(jsonReader, typeof(IQuantity), null, false, JsonSerializer.CreateDefault()); } Assert.NotNull(result); diff --git a/UnitsNet.Serialization.JsonNet.Tests/UnitsNetJsonDeserializationTests.cs b/UnitsNet.Serialization.JsonNet.Tests/UnitsNetJsonDeserializationTests.cs index 3ce95c18fb..539da9100a 100644 --- a/UnitsNet.Serialization.JsonNet.Tests/UnitsNetJsonDeserializationTests.cs +++ b/UnitsNet.Serialization.JsonNet.Tests/UnitsNetJsonDeserializationTests.cs @@ -9,208 +9,208 @@ namespace UnitsNet.Serialization.JsonNet.Tests { public sealed class UnitsNetJsonDeserializationTests : UnitsNetJsonBaseTest { - [Fact] - public void Information_CanDeserializeVeryLargeValues() + [Fact] + public void Information_CanDeserializeVeryLargeValues() + { + var original = Information.FromExabytes(1E+9); + var json = SerializeObject(original); + var deserialized = DeserializeObject(json); + + Assert.Equal(original, deserialized); + } + + [Fact] + public void Mass_ExpectJsonCorrectlyDeserialized() + { + var originalMass = Mass.FromKilograms(33.33); + var json = SerializeObject(originalMass); + + var deserializedMass = DeserializeObject(json); + + Assert.Equal(originalMass, deserializedMass); + } + + [Fact] + public void NonNullNullableValue_ExpectValueDeserializedCorrectly() + { + Mass? nullableMass = Mass.FromKilograms(10); + var json = SerializeObject(nullableMass); + + var deserializedNullableMass = DeserializeObject(json); + + Assert.Equal(nullableMass.Value, deserializedNullableMass); + } + + [Fact] + public void NonNullNullableValueNestedInObject_ExpectValueDeserializedCorrectly() + { + var testObj = new TestObject() { - var original = Information.FromExabytes(1E+9); - var json = SerializeObject(original); - var deserialized = DeserializeObject(json); + NullableFrequency = Frequency.FromHertz(10), + NonNullableFrequency = Frequency.FromHertz(10) + }; + var json = SerializeObject(testObj); - Assert.Equal(original, deserialized); - } + var deserializedTestObj = DeserializeObject(json); - [Fact] - public void Mass_ExpectJsonCorrectlyDeserialized() - { - var originalMass = Mass.FromKilograms(33.33); - var json = SerializeObject(originalMass); + Assert.Equal(testObj.NullableFrequency, deserializedTestObj.NullableFrequency); + } - var deserializedMass = DeserializeObject(json); + [Fact] + public void NullValue_ExpectNullReturned() + { + var json = SerializeObject(null); + var deserializedNullMass = DeserializeObject(json); - Assert.Equal(originalMass, deserializedMass); - } + Assert.Null(deserializedNullMass); + } - [Fact] - public void NonNullNullableValue_ExpectValueDeserializedCorrectly() + [Fact] + public void NullValueNestedInObject_ExpectValueDeserializedToNullCorrectly() + { + var testObj = new TestObject() { - Mass? nullableMass = Mass.FromKilograms(10); - var json = SerializeObject(nullableMass); - - var deserializedNullableMass = DeserializeObject(json); + NullableFrequency = null, + NonNullableFrequency = Frequency.FromHertz(10) + }; + var json = SerializeObject(testObj); - Assert.Equal(nullableMass.Value, deserializedNullableMass); - } - - [Fact] - public void NonNullNullableValueNestedInObject_ExpectValueDeserializedCorrectly() - { - var testObj = new TestObject() - { - NullableFrequency = Frequency.FromHertz(10), - NonNullableFrequency = Frequency.FromHertz(10) - }; - var json = SerializeObject(testObj); + var deserializedTestObj = DeserializeObject(json); - var deserializedTestObj = DeserializeObject(json); - - Assert.Equal(testObj.NullableFrequency, deserializedTestObj.NullableFrequency); - } - - [Fact] - public void NullValue_ExpectNullReturned() - { - var json = SerializeObject(null); - var deserializedNullMass = DeserializeObject(json); + Assert.Null(deserializedTestObj.NullableFrequency); + } - Assert.Null(deserializedNullMass); - } + [Fact] + public void UnitEnumChangedAfterSerialization_ExpectUnitCorrectlyDeserialized() + { + var originalMass = Mass.FromKilograms(33.33); + var json = SerializeObject(originalMass); - [Fact] - public void NullValueNestedInObject_ExpectValueDeserializedToNullCorrectly() - { - var testObj = new TestObject() - { - NullableFrequency = null, - NonNullableFrequency = Frequency.FromHertz(10) - }; - var json = SerializeObject(testObj); + // Someone manually changed the serialized JSON string to 1000 grams. + json = json.Replace("33.33", "1000"); + json = json.Replace("MassUnit.Kilogram", "MassUnit.Gram"); - var deserializedTestObj = DeserializeObject(json); + var deserializedMass = DeserializeObject(json); - Assert.Null(deserializedTestObj.NullableFrequency); - } + // The original value serialized was 33.33 kg, but someone edited the JSON to be 1000 g. We expect the JSON is + // still deserializable, and the correct value of 1000 g is obtained. + Assert.Equal(1000, deserializedMass.Grams); + } - [Fact] - public void UnitEnumChangedAfterSerialization_ExpectUnitCorrectlyDeserialized() + [Fact] + public void UnitInIComparable_ExpectUnitCorrectlyDeserialized() + { + var testObjWithIComparable = new TestObjectWithIComparable() { - var originalMass = Mass.FromKilograms(33.33); - var json = SerializeObject(originalMass); + Value = Power.FromWatts(10) + }; - // Someone manually changed the serialized JSON string to 1000 grams. - json = json.Replace("33.33", "1000"); - json = json.Replace("MassUnit.Kilogram", "MassUnit.Gram"); + var json = SerializeObject(testObjWithIComparable); - var deserializedMass = DeserializeObject(json); + var deserializedTestObject = DeserializeObject(json); - // The original value serialized was 33.33 kg, but someone edited the JSON to be 1000 g. We expect the JSON is - // still deserializable, and the correct value of 1000 g is obtained. - Assert.Equal(1000, deserializedMass.Grams); - } + Assert.IsType(deserializedTestObject.Value); + Assert.Equal(Power.FromWatts(10), (Power)deserializedTestObject.Value); + } - [Fact] - public void UnitInIComparable_ExpectUnitCorrectlyDeserialized() + [Fact] + public void DoubleInIComparable_ExpectUnitCorrectlyDeserialized() + { + var testObjWithIComparable = new TestObjectWithIComparable() { - var testObjWithIComparable = new TestObjectWithIComparable() - { - Value = Power.FromWatts(10) - }; - - var json = SerializeObject(testObjWithIComparable); + Value = 10.0 + }; - var deserializedTestObject = DeserializeObject(json); + var json = SerializeObject(testObjWithIComparable); + var deserializedTestObject = DeserializeObject(json); - Assert.IsType(deserializedTestObject.Value); - Assert.Equal(Power.FromWatts(10), (Power)deserializedTestObject.Value); - } + Assert.IsType(deserializedTestObject.Value); + Assert.Equal(10d, (double)deserializedTestObject.Value); + } - [Fact] - public void DoubleInIComparable_ExpectUnitCorrectlyDeserialized() + [Fact] + public void ClassInIComparable_ExpectUnitCorrectlyDeserialized() + { + var testObjWithIComparable = new TestObjectWithIComparable() { - var testObjWithIComparable = new TestObjectWithIComparable() - { - Value = 10.0 - }; - - var json = SerializeObject(testObjWithIComparable); - var deserializedTestObject = DeserializeObject(json); - - Assert.IsType(deserializedTestObject.Value); - Assert.Equal(10d, (double)deserializedTestObject.Value); - } - - [Fact] - public void ClassInIComparable_ExpectUnitCorrectlyDeserialized() - { - var testObjWithIComparable = new TestObjectWithIComparable() - { - Value = new TestObjectWithValueAsIComparable() { Value = 10 } - }; + Value = new TestObjectWithValueAsIComparable() { Value = 10 } + }; - var json = SerializeObject(testObjWithIComparable, TypeNameHandling.Auto); - var deserializedTestObject = DeserializeObject(json, TypeNameHandling.Auto); + var json = SerializeObject(testObjWithIComparable, TypeNameHandling.Auto); + var deserializedTestObject = DeserializeObject(json, TypeNameHandling.Auto); - Assert.IsType(deserializedTestObject.Value); - Assert.Equal(10d, ((TestObjectWithValueAsIComparable) deserializedTestObject.Value).Value); - } + Assert.IsType(deserializedTestObject.Value); + Assert.Equal(10d, ((TestObjectWithValueAsIComparable)deserializedTestObject.Value).Value); + } - [Fact] - public void OtherObjectWithUnitAndValue_ExpectCorrectReturnValues() - { - var testObjWithValueAndUnit = new TestObjectWithValueAndUnitAsIComparable() - { - Value = 5, - Unit = "Test", - }; - - var json = SerializeObject(testObjWithValueAndUnit); - var deserializedTestObject = DeserializeObject(json); - - Assert.IsType(deserializedTestObject.Value); - Assert.Equal(5d, deserializedTestObject.Value); - Assert.Equal("Test", deserializedTestObject.Unit); - } - - [Fact] - public void ThreeObjectsInIComparableWithDifferentValues_ExpectAllCorrectlyDeserialized() + [Fact] + public void OtherObjectWithUnitAndValue_ExpectCorrectReturnValues() + { + var testObjWithValueAndUnit = new TestObjectWithValueAndUnitAsIComparable() { - var testObjWithIComparable = new TestObjectWithThreeIComparable() - { - Value1 = 10.0, - Value2 = Power.FromWatts(19), - Value3 = new TestObjectWithValueAsIComparable() { Value = 10 }, - }; - var json = SerializeObject(testObjWithIComparable, TypeNameHandling.Auto); - var deserializedTestObject = DeserializeObject(json, TypeNameHandling.Auto); - - Assert.IsType(deserializedTestObject.Value1); - Assert.Equal(10d, deserializedTestObject.Value1); - - Assert.IsType(deserializedTestObject.Value2); - Assert.Equal(Power.FromWatts(19), deserializedTestObject.Value2); - - Assert.IsType(deserializedTestObject.Value3); - Assert.Equal(testObjWithIComparable.Value3, deserializedTestObject.Value3); - } - - [Fact] - public void ArrayOfUnits_ExpectCorrectlyDeserialized() + Value = 5, + Unit = "Test", + }; + + var json = SerializeObject(testObjWithValueAndUnit); + var deserializedTestObject = DeserializeObject(json); + + Assert.IsType(deserializedTestObject.Value); + Assert.Equal(5d, deserializedTestObject.Value); + Assert.Equal("Test", deserializedTestObject.Unit); + } + + [Fact] + public void ThreeObjectsInIComparableWithDifferentValues_ExpectAllCorrectlyDeserialized() + { + var testObjWithIComparable = new TestObjectWithThreeIComparable() { - Frequency[] expected = { Frequency.FromHertz(10), Frequency.FromHertz(10) }; - - var json = "[\n" + - " {\n" + - " \"Unit\": \"FrequencyUnit.Hertz\",\n" + - " \"Value\": 10.0\n" + - " },\n" + - " {\n" + - " \"Unit\": \"FrequencyUnit.Hertz\",\n" + - " \"Value\": 10.0\n" + - " }\n" + - "]"; - - var result = DeserializeObject(json); - - Assert.Equal(expected, result); - } - - [Fact] - public void EmptyArray_ExpectCorrectlyDeserialized() - { - var json = "[]"; - - var result = DeserializeObject(json); - - Assert.Empty(result); - } + Value1 = 10.0, + Value2 = Power.FromWatts(19), + Value3 = new TestObjectWithValueAsIComparable() { Value = 10 }, + }; + var json = SerializeObject(testObjWithIComparable, TypeNameHandling.Auto); + var deserializedTestObject = DeserializeObject(json, TypeNameHandling.Auto); + + Assert.IsType(deserializedTestObject.Value1); + Assert.Equal(10d, deserializedTestObject.Value1); + + Assert.IsType(deserializedTestObject.Value2); + Assert.Equal(Power.FromWatts(19), deserializedTestObject.Value2); + + Assert.IsType(deserializedTestObject.Value3); + Assert.Equal(testObjWithIComparable.Value3, deserializedTestObject.Value3); + } + + [Fact] + public void ArrayOfUnits_ExpectCorrectlyDeserialized() + { + Frequency[] expected = { Frequency.FromHertz(10), Frequency.FromHertz(10) }; + + var json = "[\n" + + " {\n" + + " \"Unit\": \"FrequencyUnit.Hertz\",\n" + + " \"Value\": 10.0\n" + + " },\n" + + " {\n" + + " \"Unit\": \"FrequencyUnit.Hertz\",\n" + + " \"Value\": 10.0\n" + + " }\n" + + "]"; + + var result = DeserializeObject(json); + + Assert.Equal(expected, result); + } + + [Fact] + public void EmptyArray_ExpectCorrectlyDeserialized() + { + var json = "[]"; + + var result = DeserializeObject(json); + + Assert.Empty(result); + } } } diff --git a/UnitsNet.Serialization.JsonNet.Tests/UnitsNetJsonSerializationTests.cs b/UnitsNet.Serialization.JsonNet.Tests/UnitsNetJsonSerializationTests.cs index 879a5c6942..7747a9bc9f 100644 --- a/UnitsNet.Serialization.JsonNet.Tests/UnitsNetJsonSerializationTests.cs +++ b/UnitsNet.Serialization.JsonNet.Tests/UnitsNetJsonSerializationTests.cs @@ -9,125 +9,125 @@ namespace UnitsNet.Serialization.JsonNet.Tests public sealed class UnitsNetJsonSerializationTests : UnitsNetJsonBaseTest { [Fact] - public void Information_CanSerializeVeryLargeValues() - { - Information i = Information.FromExabytes(1E+9); - var expectedJson = "{\n \"Unit\": \"InformationUnit.Exabyte\",\n \"Value\": 1000000000.0\n}"; + public void Information_CanSerializeVeryLargeValues() + { + Information i = Information.FromExabytes(1E+9); + var expectedJson = "{\n \"Unit\": \"InformationUnit.Exabyte\",\n \"Value\": 1000000000.0\n}"; - string json = SerializeObject(i); + string json = SerializeObject(i); - Assert.Equal(expectedJson, json); - } + Assert.Equal(expectedJson, json); + } - [Fact] - public void Mass_ExpectConstructedValueAndUnit() - { - Mass mass = Mass.FromPounds(200); - var expectedJson = "{\n \"Unit\": \"MassUnit.Pound\",\n \"Value\": 200.0\n}"; + [Fact] + public void Mass_ExpectConstructedValueAndUnit() + { + Mass mass = Mass.FromPounds(200); + var expectedJson = "{\n \"Unit\": \"MassUnit.Pound\",\n \"Value\": 200.0\n}"; - string json = SerializeObject(mass); + string json = SerializeObject(mass); - Assert.Equal(expectedJson, json); - } + Assert.Equal(expectedJson, json); + } - [Fact] - public void Information_ExpectConstructedValueAndUnit() - { - Information quantity = Information.FromKilobytes(54); - var expectedJson = "{\n \"Unit\": \"InformationUnit.Kilobyte\",\n \"Value\": 54.0\n}"; + [Fact] + public void Information_ExpectConstructedValueAndUnit() + { + Information quantity = Information.FromKilobytes(54); + var expectedJson = "{\n \"Unit\": \"InformationUnit.Kilobyte\",\n \"Value\": 54.0\n}"; - string json = SerializeObject(quantity); + string json = SerializeObject(quantity); - Assert.Equal(expectedJson, json); - } + Assert.Equal(expectedJson, json); + } - [Fact] - public void NonNullNullableValue_ExpectJsonUnaffected() - { - Mass? nullableMass = Mass.FromKilograms(10); - var expectedJson = "{\n \"Unit\": \"MassUnit.Kilogram\",\n \"Value\": 10.0\n}"; + [Fact] + public void NonNullNullableValue_ExpectJsonUnaffected() + { + Mass? nullableMass = Mass.FromKilograms(10); + var expectedJson = "{\n \"Unit\": \"MassUnit.Kilogram\",\n \"Value\": 10.0\n}"; - string json = SerializeObject(nullableMass); + string json = SerializeObject(nullableMass); - // There shouldn't be any change in the JSON for the non-null nullable value. - Assert.Equal(expectedJson, json); - } + // There shouldn't be any change in the JSON for the non-null nullable value. + Assert.Equal(expectedJson, json); + } - [Fact] - public void NonNullNullableValueNestedInObject_ExpectJsonUnaffected() - { - var testObj = new TestObject() - { - NullableFrequency = Frequency.FromHertz(10), - NonNullableFrequency = Frequency.FromHertz(10) - }; - - // Ugly manually formatted JSON string is used because string literals with newlines are rendered differently - // on the build server (i.e. the build server uses '\r' instead of '\n') - string expectedJson = "{\n" + - " \"NullableFrequency\": {\n" + - " \"Unit\": \"FrequencyUnit.Hertz\",\n" + - " \"Value\": 10.0\n" + - " },\n" + - " \"NonNullableFrequency\": {\n" + - " \"Unit\": \"FrequencyUnit.Hertz\",\n" + - " \"Value\": 10.0\n" + - " }\n" + - "}"; - - string json = SerializeObject(testObj); - - Assert.Equal(expectedJson, json); - } - - [Fact] - public void NullValue_ExpectJsonContainsNullString() + [Fact] + public void NonNullNullableValueNestedInObject_ExpectJsonUnaffected() + { + var testObj = new TestObject() { - string json = SerializeObject(null); - Assert.Equal("null", json); - } + NullableFrequency = Frequency.FromHertz(10), + NonNullableFrequency = Frequency.FromHertz(10) + }; + + // Ugly manually formatted JSON string is used because string literals with newlines are rendered differently + // on the build server (i.e. the build server uses '\r' instead of '\n') + string expectedJson = "{\n" + + " \"NullableFrequency\": {\n" + + " \"Unit\": \"FrequencyUnit.Hertz\",\n" + + " \"Value\": 10.0\n" + + " },\n" + + " \"NonNullableFrequency\": {\n" + + " \"Unit\": \"FrequencyUnit.Hertz\",\n" + + " \"Value\": 10.0\n" + + " }\n" + + "}"; + + string json = SerializeObject(testObj); + + Assert.Equal(expectedJson, json); + } - [Fact] - public void Ratio_ExpectDecimalFractionsUsedAsBaseValueAndUnit() - { - Ratio ratio = Ratio.FromPartsPerThousand(250); - var expectedJson = "{\n \"Unit\": \"RatioUnit.PartPerThousand\",\n \"Value\": 250.0\n}"; + [Fact] + public void NullValue_ExpectJsonContainsNullString() + { + string json = SerializeObject(null); + Assert.Equal("null", json); + } - string json = SerializeObject(ratio); + [Fact] + public void Ratio_ExpectDecimalFractionsUsedAsBaseValueAndUnit() + { + Ratio ratio = Ratio.FromPartsPerThousand(250); + var expectedJson = "{\n \"Unit\": \"RatioUnit.PartPerThousand\",\n \"Value\": 250.0\n}"; - Assert.Equal(expectedJson, json); - } + string json = SerializeObject(ratio); - [Fact] - public void ArrayValue_ExpectJsonArray() - { - Frequency[] testObj = { Frequency.FromHertz(10), Frequency.FromHertz(10) }; - - string expectedJson = "[\n" + - " {\n" + - " \"Unit\": \"FrequencyUnit.Hertz\",\n" + - " \"Value\": 10.0\n" + - " },\n" + - " {\n" + - " \"Unit\": \"FrequencyUnit.Hertz\",\n" + - " \"Value\": 10.0\n" + - " }\n" + - "]"; - - string json = SerializeObject(testObj); - - Assert.Equal(expectedJson, json); - } - - [Fact] - public void EmptyArrayValue_ExpectJsonArray() - { - Frequency[] testObj = new Frequency[0]; + Assert.Equal(expectedJson, json); + } + + [Fact] + public void ArrayValue_ExpectJsonArray() + { + Frequency[] testObj = { Frequency.FromHertz(10), Frequency.FromHertz(10) }; + + string expectedJson = "[\n" + + " {\n" + + " \"Unit\": \"FrequencyUnit.Hertz\",\n" + + " \"Value\": 10.0\n" + + " },\n" + + " {\n" + + " \"Unit\": \"FrequencyUnit.Hertz\",\n" + + " \"Value\": 10.0\n" + + " }\n" + + "]"; + + string json = SerializeObject(testObj); + + Assert.Equal(expectedJson, json); + } + + [Fact] + public void EmptyArrayValue_ExpectJsonArray() + { + Frequency[] testObj = new Frequency[0]; - string expectedJson = "[]"; + string expectedJson = "[]"; - string json = SerializeObject(testObj); - Assert.Equal(expectedJson, json); - } + string json = SerializeObject(testObj); + Assert.Equal(expectedJson, json); + } } } diff --git a/UnitsNet.Serialization.JsonNet/UnitsNetBaseJsonConverter.cs b/UnitsNet.Serialization.JsonNet/UnitsNetBaseJsonConverter.cs index d2916dadb9..d5d1905ae3 100644 --- a/UnitsNet.Serialization.JsonNet/UnitsNetBaseJsonConverter.cs +++ b/UnitsNet.Serialization.JsonNet/UnitsNetBaseJsonConverter.cs @@ -13,7 +13,7 @@ namespace UnitsNet.Serialization.JsonNet /// Base converter for serializing and deserializing UnitsNet types to and from JSON. /// Contains shared functionality used by and /// - /// + /// The type being converted. Should either be or public abstract class UnitsNetBaseJsonConverter : JsonConverter { /// diff --git a/UnitsNet.Serialization.JsonNet/UnitsNetIComparableJsonConverter.cs b/UnitsNet.Serialization.JsonNet/UnitsNetIComparableJsonConverter.cs index dfd7e391a9..66a3b57415 100644 --- a/UnitsNet.Serialization.JsonNet/UnitsNetIComparableJsonConverter.cs +++ b/UnitsNet.Serialization.JsonNet/UnitsNetIComparableJsonConverter.cs @@ -25,11 +25,15 @@ public sealed class UnitsNetIComparableJsonConverter : UnitsNetBaseJsonConverter /// /// Serialize an IComparable to JSON. - /// Not used. + /// Not implemented. Instead is always used to serialize an IComparable when it is a UnitsNet quantity. /// /// The writer to use /// The value to serialize /// The serializer to use + /// + /// Because this converter should only be used in combination with the , the WriteJson method of that converter will always be used + /// to serialize an IComparable in the context of UnitsNet. + /// JsonNet is capable of serializing any IComparable implementation. public override void WriteJson(JsonWriter writer, IComparable value, JsonSerializer serializer) { throw new NotImplementedException("Serialization of IComparable is handled by default serialization");