diff --git a/UnitsNet.Serialization.JsonNet.CompatibilityTests/UnitsNetJsonConverterTests.cs b/UnitsNet.Serialization.JsonNet.CompatibilityTests/UnitsNetJsonConverterTests.cs index 0a62526b9c..3a71fbc5fe 100644 --- a/UnitsNet.Serialization.JsonNet.CompatibilityTests/UnitsNetJsonConverterTests.cs +++ b/UnitsNet.Serialization.JsonNet.CompatibilityTests/UnitsNetJsonConverterTests.cs @@ -351,6 +351,39 @@ public void ArrayOfUnits_ExpectCorrectlyDeserialized() Assert.Equal(expected, result); } + [Fact(Skip = "Not supported in older versions of serialization")] + public void MultiDimArrayOfUnits_ExpectCorrectlyDeserialized() + { + Frequency[,] expected = { { Frequency.FromHertz(10), Frequency.FromHertz(10) }, { Frequency.FromHertz(10), Frequency.FromHertz(10) } }; + + string json = "[\n" + + " [\n" + + " {\n" + + " \"Unit\": \"FrequencyUnit.Hertz\",\n" + + " \"Value\": 10.0\n" + + " },\n" + + " {\n" + + " \"Unit\": \"FrequencyUnit.Hertz\",\n" + + " \"Value\": 10.0\n" + + " }\n" + + " ],\n" + + " [\n" + + " {\n" + + " \"Unit\": \"FrequencyUnit.Hertz\",\n" + + " \"Value\": 10.0\n" + + " },\n" + + " {\n" + + " \"Unit\": \"FrequencyUnit.Hertz\",\n" + + " \"Value\": 10.0\n" + + " }\n" + + " ]\n" + + "]"; + + Frequency[,] result = DeserializeObject(json); + + Assert.Equal(expected, result); + } + [Fact(Skip = "Not supported in older versions of serialization")] public void EmptyArray_ExpectCorrectlyDeserialized() { diff --git a/UnitsNet.Serialization.JsonNet.Tests/UnitsNetJsonDeserializationTests.cs b/UnitsNet.Serialization.JsonNet.Tests/UnitsNetJsonDeserializationTests.cs index 539da9100a..610f45c976 100644 --- a/UnitsNet.Serialization.JsonNet.Tests/UnitsNetJsonDeserializationTests.cs +++ b/UnitsNet.Serialization.JsonNet.Tests/UnitsNetJsonDeserializationTests.cs @@ -188,21 +188,54 @@ 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" + - "]"; + " {\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 MultiDimArrayOfUnits_ExpectCorrectlyDeserialized() + { + Frequency[,] expected = { { Frequency.FromHertz(10), Frequency.FromHertz(10) }, { Frequency.FromHertz(10), Frequency.FromHertz(10) } }; + + var json = "[\n" + + " [\n" + + " {\n" + + " \"Unit\": \"FrequencyUnit.Hertz\",\n" + + " \"Value\": 10.0\n" + + " },\n" + + " {\n" + + " \"Unit\": \"FrequencyUnit.Hertz\",\n" + + " \"Value\": 10.0\n" + + " }\n" + + " ],\n" + + " [\n" + + " {\n" + + " \"Unit\": \"FrequencyUnit.Hertz\",\n" + + " \"Value\": 10.0\n" + + " },\n" + + " {\n" + + " \"Unit\": \"FrequencyUnit.Hertz\",\n" + + " \"Value\": 10.0\n" + + " }\n" + + " ]\n" + + "]"; + + var result = DeserializeObject(json); + + Assert.Equal(expected, result); + } + [Fact] public void EmptyArray_ExpectCorrectlyDeserialized() { diff --git a/UnitsNet.Serialization.JsonNet.Tests/UnitsNetJsonSerializationTests.cs b/UnitsNet.Serialization.JsonNet.Tests/UnitsNetJsonSerializationTests.cs index 7747a9bc9f..537b7d3c92 100644 --- a/UnitsNet.Serialization.JsonNet.Tests/UnitsNetJsonSerializationTests.cs +++ b/UnitsNet.Serialization.JsonNet.Tests/UnitsNetJsonSerializationTests.cs @@ -119,6 +119,39 @@ public void ArrayValue_ExpectJsonArray() Assert.Equal(expectedJson, json); } + [Fact] + public void MultiDimArrayValue_ExpectJsonArray() + { + Frequency[,] testObj = { { Frequency.FromHertz(10), Frequency.FromHertz(10) }, { Frequency.FromHertz(10), Frequency.FromHertz(10) } }; + + string expectedJson = "[\n" + + " [\n" + + " {\n" + + " \"Unit\": \"FrequencyUnit.Hertz\",\n" + + " \"Value\": 10.0\n" + + " },\n" + + " {\n" + + " \"Unit\": \"FrequencyUnit.Hertz\",\n" + + " \"Value\": 10.0\n" + + " }\n" + + " ],\n" + + " [\n" + + " {\n" + + " \"Unit\": \"FrequencyUnit.Hertz\",\n" + + " \"Value\": 10.0\n" + + " },\n" + + " {\n" + + " \"Unit\": \"FrequencyUnit.Hertz\",\n" + + " \"Value\": 10.0\n" + + " }\n" + + " ]\n" + + "]"; + + string json = SerializeObject(testObj); + + Assert.Equal(expectedJson, json); + } + [Fact] public void EmptyArrayValue_ExpectJsonArray() { diff --git a/UnitsNet.Serialization.JsonNet/Internal/MultiDimensionalArrayHelpers.cs b/UnitsNet.Serialization.JsonNet/Internal/MultiDimensionalArrayHelpers.cs new file mode 100644 index 0000000000..5a675067c7 --- /dev/null +++ b/UnitsNet.Serialization.JsonNet/Internal/MultiDimensionalArrayHelpers.cs @@ -0,0 +1,89 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace UnitsNet.Serialization.JsonNet.Internal +{ + + /// + /// Helper class for working with and manipulating multi-dimension arrays based on their generic index. + /// + internal static class MultiDimensionalArrayHelpers + { + + /// + /// Returns a new array of same Rank and Length as but with each element converted to + /// + /// + /// + /// + public static Array ConvertArrayElements(Array array) + { + var ret = Array.CreateInstance(typeof(TResult), LastIndex(array)); + var ind = FirstIndex(array); + + while (ind != null) + { + ret.SetValue((TResult)array.GetValue(ind), ind); + ind = NextIndex(array, ind); + } + return ret; + } + + /// + /// Returns the index for the 'first' element in a multidimensional array. + /// + /// 'First' is defined as the for each rank of the + /// + /// E.g., for a zero-based 5x5x5 array this method would return [0, 0, 0]. + /// + /// + /// 1D integer array specifying the location of the first element in the multidimensional array + public static int[] FirstIndex(Array array) + { + return Enumerable.Range(0, array.Rank).Select(x => array.GetLowerBound(x)).ToArray(); + } + + /// + /// Returns the index for the 'last' element in a multidimensional array. + /// + /// 'Last' is defined as the for each rank of the + /// + /// E.g., for a zero-based 5x5x5 array this method would return [4, 4, 4]. + /// + /// + /// 1D integer array specifying the location of the last element in the multidimensional array + public static int[] LastIndex(Array array) + { + return Enumerable.Range(0, array.Rank).Select(x => array.GetUpperBound(x) + 1).ToArray(); + } + + /// + /// Returns the 'next' index after the specified multidimensional + /// + /// The 'next' index is determined by first looping through all elements in the first dimension of the array, then moving on to the next dimension and repeating + /// + /// + /// + /// Returns the index location of the next element in after as a 1D array of integers. If there is no next index, returns null + public static int[] NextIndex(Array array, int[] index) + { + for (var i = 0; i < index.Length; i++) + { + index[i] += 1; + + if (index[i] <= array.GetUpperBound(i)) + { + return index; + } + else + { + index[i] = array.GetLowerBound(i); + } + } + return null; + } + + } +} diff --git a/UnitsNet.Serialization.JsonNet/UnitsNetJsonConverter.cs b/UnitsNet.Serialization.JsonNet/UnitsNetJsonConverter.cs index 217b76ea9e..2782da5ed8 100644 --- a/UnitsNet.Serialization.JsonNet/UnitsNetJsonConverter.cs +++ b/UnitsNet.Serialization.JsonNet/UnitsNetJsonConverter.cs @@ -6,6 +6,7 @@ using JetBrains.Annotations; using Newtonsoft.Json; using Newtonsoft.Json.Linq; +using UnitsNet.Serialization.JsonNet.Internal; namespace UnitsNet.Serialization.JsonNet { @@ -44,15 +45,15 @@ public override object ReadJson(JsonReader reader, Type objectType, object exist object obj = TryDeserializeIComparable(reader, serializer); if (obj is Array values) { - // Create array with the requested type, such as `Length[]` or `Frequency[]` - var arrayOfQuantities = Array.CreateInstance(objectType.GetElementType(), values.Length); + + // Create array with the requested type, such as `Length[]` or `Frequency[]` or multi-dimensional arrays like `Length[,]` or `Frequency[,,]` + var arrayOfQuantities = Array.CreateInstance(objectType.GetElementType(), MultiDimensionalArrayHelpers.LastIndex(values)); // Fill array with parsed quantities - var i = 0; - foreach (ValueUnit valueUnit in values) + int[] index = MultiDimensionalArrayHelpers.FirstIndex(values); + while (index != null) { - IQuantity quantity = ParseValueUnit(valueUnit); - arrayOfQuantities.SetValue(quantity, i++); + arrayOfQuantities.SetValue(values.GetValue(index), index); } return arrayOfQuantities; @@ -148,7 +149,16 @@ public override void WriteJson(JsonWriter writer, object obj, JsonSerializer ser } else if (obj is Array values) { - var results = values.Cast().Select(ToValueUnit); + + var results = Array.CreateInstance(typeof(ValueUnit), MultiDimensionalArrayHelpers.LastIndex(values)); + var ind = MultiDimensionalArrayHelpers.FirstIndex(values); + + while (ind != null) + { + results.SetValue((IQuantity)values.GetValue(ind), ind); + ind = MultiDimensionalArrayHelpers.NextIndex(results, ind); + } + serializer.Serialize(writer, results); } else if (obj is IQuantity quantity) @@ -231,5 +241,6 @@ protected virtual bool CanConvertNullable(Type objectType) } #endregion + } }