From f7273023cca9efdd07fcf6b4fefa46acaa66aef0 Mon Sep 17 00:00:00 2001 From: Marcin Torba Date: Thu, 20 Oct 2022 11:32:57 +0200 Subject: [PATCH] Allow deserialization when properties case doesn't match (#251) --- README.md | 5 + .../ConvertersMappingTests.cs | 5 +- .../JsonCustomTypeTests.cs | 9 +- .../Configuration/SettingsTests.cs | 23 +++ .../Converters/BoolConverterTests.cs | 10 +- .../Converters/DateTimeConverterTests.cs | 20 ++ .../Converters/DoubleConverterTests.cs | 9 + .../Converters/FloatConverterTests.cs | 9 + .../Converters/StringConverterTests.cs | 14 +- .../Converters/TimeSpanConverterTests.cs | 2 + .../JsonDeserializationArraysTests.cs | 37 ++++ nanoFramework.Json.Test/JsonUnitTests.cs | 40 +--- ...erResolverCaseInsensitiveExceptionTests.cs | 67 +++++++ .../MemberResolverCaseInsensitiveTests.cs | 74 ++++++++ ...mberResolverCaseSensitiveExceptionTests.cs | 65 +++++++ .../MemberResolverCaseSensitiveTests.cs | 71 +++++++ .../nanoFramework.Json.Test.nfproj | 11 +- .../ConvertersMapping.cs | 5 +- nanoFramework.Json/Configuration/Settings.cs | 32 ++++ .../Converters/BoolConverter.cs | 2 +- .../Converters/DateTimeConverter.cs | 12 +- .../Converters/DoubleConverter.cs | 14 +- .../Converters/FloatConverter.cs | 13 +- .../Converters/StringConverter.cs | 44 +++-- .../Converters/TimeSpanConverter.cs | 2 +- nanoFramework.Json/JsonConvert.cs | 174 ++++-------------- nanoFramework.Json/JsonSerializer.cs | 1 + .../Resolvers/IMemberResolver.cs | 24 +++ .../Resolvers/MemberResolver.cs | 111 +++++++++++ nanoFramework.Json/Resolvers/MemberSet.cs | 61 ++++++ nanoFramework.Json/nanoFramework.Json.nfproj | 6 +- 31 files changed, 752 insertions(+), 220 deletions(-) rename nanoFramework.Json.Test/{Converters => Configuration}/ConvertersMappingTests.cs (95%) rename nanoFramework.Json.Test/{ => Configuration}/JsonCustomTypeTests.cs (85%) create mode 100644 nanoFramework.Json.Test/Configuration/SettingsTests.cs create mode 100644 nanoFramework.Json.Test/Converters/DateTimeConverterTests.cs create mode 100644 nanoFramework.Json.Test/JsonDeserializationArraysTests.cs create mode 100644 nanoFramework.Json.Test/Resolvers/MemberResolverCaseInsensitiveExceptionTests.cs create mode 100644 nanoFramework.Json.Test/Resolvers/MemberResolverCaseInsensitiveTests.cs create mode 100644 nanoFramework.Json.Test/Resolvers/MemberResolverCaseSensitiveExceptionTests.cs create mode 100644 nanoFramework.Json.Test/Resolvers/MemberResolverCaseSensitiveTests.cs rename nanoFramework.Json/{Converters => Configuration}/ConvertersMapping.cs (95%) create mode 100644 nanoFramework.Json/Configuration/Settings.cs create mode 100644 nanoFramework.Json/Resolvers/IMemberResolver.cs create mode 100644 nanoFramework.Json/Resolvers/MemberResolver.cs create mode 100644 nanoFramework.Json/Resolvers/MemberSet.cs diff --git a/README.md b/README.md index e1922f64..c249263c 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,11 @@ |:-|---|---| | Json | [![Build Status](https://dev.azure.com/nanoframework/nanoFramework.Json/_apis/build/status/nanoFramework.Json?repoName=nanoframework%2FnanoFramework.Json&branchName=main)](https://dev.azure.com/nanoframework/nanoFramework.Json/_build/latest?definitionId=59&repoName=nanoframework%2FnanoFramework.Json&branchName=main) | [![NuGet](https://img.shields.io/nuget/v/nanoFramework.Json.svg?label=NuGet&style=flat&logo=nuget)](https://www.nuget.org/packages/nanoFramework.Json/) | +## Case insensitive members resolving +By default nanoFramework.JSON deserialization is case sensitive. This behaviour can be switched by changing nanoFramework.Json.Configuration.Settings.CaseSensitive flag to false. +Note, case insensitive resolving is 3-5 times slower than case sensitive. Keep that in mind before using it. + + ## Feedback and documentation For documentation, providing feedback, issues and finding out how to contribute please refer to the [Home repo](https://github.com/nanoframework/Home). diff --git a/nanoFramework.Json.Test/Converters/ConvertersMappingTests.cs b/nanoFramework.Json.Test/Configuration/ConvertersMappingTests.cs similarity index 95% rename from nanoFramework.Json.Test/Converters/ConvertersMappingTests.cs rename to nanoFramework.Json.Test/Configuration/ConvertersMappingTests.cs index 4592922b..062955b4 100644 --- a/nanoFramework.Json.Test/Converters/ConvertersMappingTests.cs +++ b/nanoFramework.Json.Test/Configuration/ConvertersMappingTests.cs @@ -1,9 +1,10 @@ -using nanoFramework.Json.Converters; +using nanoFramework.Json.Configuration; +using nanoFramework.Json.Converters; using nanoFramework.TestFramework; using System; using System.Text; -namespace nanoFramework.Json.Test.Converters +namespace nanoFramework.Json.Test.Configuration { [TestClass] public class ConvertersMappingTests diff --git a/nanoFramework.Json.Test/JsonCustomTypeTests.cs b/nanoFramework.Json.Test/Configuration/JsonCustomTypeTests.cs similarity index 85% rename from nanoFramework.Json.Test/JsonCustomTypeTests.cs rename to nanoFramework.Json.Test/Configuration/JsonCustomTypeTests.cs index 2fd1dc8e..382f3eca 100644 --- a/nanoFramework.Json.Test/JsonCustomTypeTests.cs +++ b/nanoFramework.Json.Test/Configuration/JsonCustomTypeTests.cs @@ -1,9 +1,10 @@ -using nanoFramework.Json.Converters; +using nanoFramework.Json.Configuration; +using nanoFramework.Json.Converters; using nanoFramework.TestFramework; using System; using System.Text; -namespace nanoFramework.Json.Test +namespace nanoFramework.Json.Test.Configuration { [TestClass] public class JsonCustomTypeTests @@ -28,13 +29,13 @@ public object ToType(object value) } [Setup] - public void Setup() + public void JsonCustomTypeTests_Setup() { ConvertersMapping.Add(typeof(TestObject), new CustomConverter()); } [Cleanup] - public void CleanUp() + public void JsonCustomTypeTests_CleanUp() { ConvertersMapping.Remove(typeof(TestObject)); } diff --git a/nanoFramework.Json.Test/Configuration/SettingsTests.cs b/nanoFramework.Json.Test/Configuration/SettingsTests.cs new file mode 100644 index 00000000..638374eb --- /dev/null +++ b/nanoFramework.Json.Test/Configuration/SettingsTests.cs @@ -0,0 +1,23 @@ +using nanoFramework.Json.Configuration; +using nanoFramework.TestFramework; +using System; +using System.Text; + +namespace nanoFramework.Json.Test.Configuration +{ + [TestClass] + public class SettingsTests + { + [TestMethod] + public void CaseSensitive_Should_BeTrueByDefault() + { + Assert.Equal(Settings.CaseSensitive, true); + } + + [TestMethod] + public void ThrowExceptionWhenPropertyNotFound_Should_BeFalseByDefault() + { + Assert.Equal(Settings.ThrowExceptionWhenPropertyNotFound, false); + } + } +} diff --git a/nanoFramework.Json.Test/Converters/BoolConverterTests.cs b/nanoFramework.Json.Test/Converters/BoolConverterTests.cs index e2f30725..704cf947 100644 --- a/nanoFramework.Json.Test/Converters/BoolConverterTests.cs +++ b/nanoFramework.Json.Test/Converters/BoolConverterTests.cs @@ -6,16 +6,16 @@ namespace nanoFramework.Json.Test.Converters [TestClass] public class BoolConverterTests { - /*[TestMethod] - [DataRow("true", true)] - [DataRow("false", false)] - public void ToType_ShouldReturnValidData(string value, bool expectedValue) + [TestMethod] + [DataRow(true, true)] + [DataRow(false, false)] + public void BoolConverter_ToType_ShouldReturnValidData(object value, bool expectedValue) { var converter = new Json.Converters.BoolConverter(); var convertedValue = (bool)converter.ToType(value); Assert.Equal(expectedValue, convertedValue); - }*/ + } [TestMethod] [DataRow(true, "true")] diff --git a/nanoFramework.Json.Test/Converters/DateTimeConverterTests.cs b/nanoFramework.Json.Test/Converters/DateTimeConverterTests.cs new file mode 100644 index 00000000..c9024fb7 --- /dev/null +++ b/nanoFramework.Json.Test/Converters/DateTimeConverterTests.cs @@ -0,0 +1,20 @@ +using nanoFramework.TestFramework; +using System; +using System.Text; + +namespace nanoFramework.Json.Test.Converters +{ + [TestClass] + public class DateTimeConverterTests + { + [TestMethod] + [DataRow(0, "\"1970-01-01T00:00:00.0000000Z\"")] + public void DateTimeConverter_BoolConverter_ToJson_Should_ReturnValidData(int value, string expectedValue) + { + var converter = new Json.Converters.DateTimeConverter(); + var convertedValue = converter.ToJson(DateTime.FromUnixTimeSeconds(value)); + + Assert.Equal(expectedValue, convertedValue); + } + } +} diff --git a/nanoFramework.Json.Test/Converters/DoubleConverterTests.cs b/nanoFramework.Json.Test/Converters/DoubleConverterTests.cs index e88e0195..5b63541d 100644 --- a/nanoFramework.Json.Test/Converters/DoubleConverterTests.cs +++ b/nanoFramework.Json.Test/Converters/DoubleConverterTests.cs @@ -17,6 +17,15 @@ public void DoubleConverter_ToType_ShouldReturnValidData(string value, double ex Assert.Equal(expectedValue, convertedValue); } + [TestMethod] + public void DoubleConverter_ToType_NullShouldReturnNaN() + { + var converter = new Json.Converters.DoubleConverter(); + var convertedValue = (double)converter.ToType(null); + + Assert.Equal(double.NaN.ToString(), convertedValue.ToString()); + } + [TestMethod] [DataRow(120.5, "120.5")] [DataRow(42.5, "42.5")] diff --git a/nanoFramework.Json.Test/Converters/FloatConverterTests.cs b/nanoFramework.Json.Test/Converters/FloatConverterTests.cs index 4ff47941..df031fdd 100644 --- a/nanoFramework.Json.Test/Converters/FloatConverterTests.cs +++ b/nanoFramework.Json.Test/Converters/FloatConverterTests.cs @@ -17,6 +17,15 @@ public void FloatConverter_ToType_ShouldReturnValidData(string value, float expe Assert.Equal(expectedValue, convertedValue); } + [TestMethod] + public void FloatConverter_ToType_NullShouldReturnNaN() + { + var converter = new Json.Converters.FloatConverter(); + var convertedValue = (float)converter.ToType(null); + + Assert.Equal(float.NaN.ToString(), convertedValue.ToString()); + } + [TestMethod] [DataRow(120.5f, "120.5")] [DataRow(42.5f, "42.5")] diff --git a/nanoFramework.Json.Test/Converters/StringConverterTests.cs b/nanoFramework.Json.Test/Converters/StringConverterTests.cs index a1a1e36c..99f7e3e9 100644 --- a/nanoFramework.Json.Test/Converters/StringConverterTests.cs +++ b/nanoFramework.Json.Test/Converters/StringConverterTests.cs @@ -7,7 +7,8 @@ namespace nanoFramework.Json.Test.Converters public class StringConverterTests { [TestMethod] - [DataRow("\"TestJson\"", "TestJson")] + [DataRow("\"TestJson\"", "\"TestJson\"")] + [DataRow("TestJson1", "TestJson1")] public void StringConverter_ToType_ShouldReturnValidData(string value, string expectedValue) { var converter = new Json.Converters.StringConverter(); @@ -17,7 +18,16 @@ public void StringConverter_ToType_ShouldReturnValidData(string value, string ex } [TestMethod] - [DataRow("TestJson", "\"TestJson\"")] + public void StringConverter_ToType_ShouldReturnStringEmptyForNull() + { + var converter = new Json.Converters.StringConverter(); + var convertedValue = (string)converter.ToType(null); + + Assert.Equal(string.Empty, convertedValue); + } + + [TestMethod] + [DataRow("TestJson2", "\"TestJson2\"")] public void StringConverter_ToJson_Should_ReturnValidData(string value, string expectedValue) { var converter = new Json.Converters.StringConverter(); diff --git a/nanoFramework.Json.Test/Converters/TimeSpanConverterTests.cs b/nanoFramework.Json.Test/Converters/TimeSpanConverterTests.cs index 2f8d992e..9329cc59 100644 --- a/nanoFramework.Json.Test/Converters/TimeSpanConverterTests.cs +++ b/nanoFramework.Json.Test/Converters/TimeSpanConverterTests.cs @@ -8,6 +8,7 @@ public class TimeSpanConverterTests { [TestMethod] [DataRow("10:00:00", 10)] + [DataRow("24:00:00", 24)] public void TimeSpanConverter_ToType_ShouldReturnValidData(string value, int expectedValueHours) { var converter = new Json.Converters.TimeSpanConverter(); @@ -19,6 +20,7 @@ public void TimeSpanConverter_ToType_ShouldReturnValidData(string value, int exp [TestMethod] [DataRow(10, "\"10:00:00\"")] + [DataRow(24, "\"24:00:00\"")] public void TimeSpanConverter_ToJson_Should_ReturnValidData(int valueHours, string expectedValue) { var converter = new Json.Converters.TimeSpanConverter(); diff --git a/nanoFramework.Json.Test/JsonDeserializationArraysTests.cs b/nanoFramework.Json.Test/JsonDeserializationArraysTests.cs new file mode 100644 index 00000000..73976593 --- /dev/null +++ b/nanoFramework.Json.Test/JsonDeserializationArraysTests.cs @@ -0,0 +1,37 @@ +using nanoFramework.TestFramework; +using System; +using System.Text; + +namespace nanoFramework.Json.Test +{ + [TestClass] + public class JsonDeserializationArraysTests + { + const string IntArrayJson = "[405421362,1082483948,1131707654,345242860,1111968802]"; + const string ShortArrayJson = "[12345,25463,22546,18879,12453]"; + + [TestMethod] + public void CanDeserializeIntArray() + { + var result = (int[])JsonConvert.DeserializeObject(IntArrayJson, typeof(int[])); + + Assert.Equal(result[0], 405421362); + Assert.Equal(result[1], 1082483948); + Assert.Equal(result[2], 1131707654); + Assert.Equal(result[3], 345242860); + Assert.Equal(result[4], 1111968802); + } + + [TestMethod] + public void CanDeserializeShortArray() + { + var result = (short[])JsonConvert.DeserializeObject(ShortArrayJson, typeof(short[])); + + Assert.Equal(result[0], (short)12345); + Assert.Equal(result[1], (short)25463); + Assert.Equal(result[2], (short)22546); + Assert.Equal(result[3], (short)18879); + Assert.Equal(result[4], (short)12453); + } + } +} diff --git a/nanoFramework.Json.Test/JsonUnitTests.cs b/nanoFramework.Json.Test/JsonUnitTests.cs index 222bb0a5..44890460 100644 --- a/nanoFramework.Json.Test/JsonUnitTests.cs +++ b/nanoFramework.Json.Test/JsonUnitTests.cs @@ -314,42 +314,6 @@ public void Can_serialize_deserialize_timespan_01() OutputHelper.WriteLine(""); } - [TestMethod] - public void DeserialzieInvalidTimeSpan_Should_ThrowInvalidCaseException() - { - OutputHelper.WriteLine("Can_serialize_deserialize_timespan_02() - Starting test..."); - - string[] strArr = new string[] { - "{\"Duration5\":\"00:00:00\",\"Duration1\":\"01:09:00\",\"DummyValue2\":777,\"Duration2\":\"00:00:00\",\"DummyValue1\":-999,\"Duration3\":\"00:00:00\",\"Duration4\":\"24:0:0\"}", - "{\"Duration5\":\"00:00:00\",\"Duration1\":\"01:09:00\",\"DummyValue2\":777,\"Duration2\":\"00:00:00\",\"DummyValue1\":-999,\"Duration3\":\"00:00:00\",\"Duration4\":\"0:0:60\"}", - "{\"Duration5\":\"00:00:00\",\"Duration1\":\"01:09:00\",\"DummyValue2\":777,\"Duration2\":\"00:00:00\",\"DummyValue1\":-999,\"Duration3\":\"00:00:00\",\"Duration4\":\"10:\"}", - "{\"Duration5\":\"00:00:00\",\"Duration1\":\"01:09:00\",\"DummyValue2\":777,\"Duration2\":\"00:00:00\",\"DummyValue1\":-999,\"Duration3\":\"00:00:00\",\"Duration4\":\":10\"}", - "{\"Duration5\":\"00:00:00\",\"Duration1\":\"01:09:00\",\"DummyValue2\":777,\"Duration2\":\"00:00:00\",\"DummyValue1\":-999,\"Duration3\":\"00:00:00\",\"Duration4\":\"10:20:\"}", - "{\"Duration5\":\"00:00:00\",\"Duration1\":\"01:09:00\",\"DummyValue2\":777,\"Duration2\":\"00:00:00\",\"DummyValue1\":-999,\"Duration3\":\"00:00:00\",\"Duration4\":\".123\"}", - "{\"Duration5\":\"00:00:00\",\"Duration1\":\"01:09:00\",\"DummyValue2\":777,\"Duration2\":\"00:00:00\",\"DummyValue1\":-999,\"Duration3\":\"00:00:00\",\"Duration4\":\"10.\"}", - "{\"Duration5\":\"00:00:00\",\"Duration1\":\"01:09:00\",\"DummyValue2\":777,\"Duration2\":\"00:00:00\",\"DummyValue1\":-999,\"Duration3\":\"00:00:00\",\"Duration4\":\"10.12\"}", - }; - - for (int i = 0; i < strArr.Length; i++) - { - try - { - // The method should throw InvalidCaseException as each strArr has at least one invalid value for TimeSpan - JsonConvert.DeserializeObject(strArr[i], typeof(JsonTestClassTimeSpan)); - - // If the method above haven't throw InvalidCastException then the test should fail - throw new InvalidOperationException($"Should throw exception {nameof(InvalidCastException)}."); - } - catch (InvalidCastException) - { - // Deserialization should throw exception and test should not fail. - } - } - - OutputHelper.WriteLine("Can_serialize_deserialize_timespan_02() - Finished - test succeeded."); - OutputHelper.WriteLine(""); - } - [TestMethod] public void Can_serialize_short_array() { @@ -977,8 +941,8 @@ public void CanDeserializeInvocationReceiveMessage_05() string arg1 = (string)JsonConvert.DeserializeObject(JsonConvert.SerializeObject(dserResult.arguments[1]), typeof(string)); - Assert.Equal(arg0, "I_am_a_string", $"arg0 has unexpected value: {arg0}"); - Assert.Equal(arg1, "I_am_another_string", $"arg1 has unexpected value: {arg1}"); + Assert.Equal(arg0, "\"I_am_a_string\"", $"arg0 has unexpected value: {arg0}"); + Assert.Equal(arg1, "\"I_am_another_string\"", $"arg1 has unexpected value: {arg1}"); } [TestMethod] diff --git a/nanoFramework.Json.Test/Resolvers/MemberResolverCaseInsensitiveExceptionTests.cs b/nanoFramework.Json.Test/Resolvers/MemberResolverCaseInsensitiveExceptionTests.cs new file mode 100644 index 00000000..2e674ec8 --- /dev/null +++ b/nanoFramework.Json.Test/Resolvers/MemberResolverCaseInsensitiveExceptionTests.cs @@ -0,0 +1,67 @@ +using nanoFramework.Json.Configuration; +using nanoFramework.Json.Resolvers; +using nanoFramework.TestFramework; +using System; + +namespace nanoFramework.Json.Test.Resolvers +{ + [TestClass] + public class MemberResolverCaseInsensitiveExceptionTests + { + private class TestClass + { + public int NoGetProperty { private get; set; } = 1; + public int NoSetProperty { get; } = 1; + } + + [Setup] + public void MemberResolverCaseInsensitiveExceptionTests_Setup() + { + Settings.ThrowExceptionWhenPropertyNotFound = true; + Settings.CaseSensitive = false; + } + + [Cleanup] + public void MemberResolverCaseInsensitiveExceptionTests_Cleanup() + { + Settings.ThrowExceptionWhenPropertyNotFound = false; + Settings.CaseSensitive = true; + } + + [TestMethod] + public void MemberResolverCaseInsensitiveExceptionTests_Get_ShouldSkipPropertyWithoutGet() + { + var resolver = new MemberResolver(); + + try + { + resolver.Get(nameof(TestClass.NoGetProperty), typeof(TestClass)); + } + catch (DeserializationException) + { + // Intended. Method should throw this type of exception when no set method. + return; + } + + throw new InvalidOperationException($"Should throw {nameof(DeserializationException)}."); + } + + [TestMethod] + public void MemberResolverCaseInsensitiveExceptionTests_Get_ShouldSkipPropertyWithoutSet() + { + var resolver = new MemberResolver(); + + try + { + resolver.Get(nameof(TestClass.NoSetProperty), typeof(TestClass)); + } + catch (DeserializationException) + { + // Intended. Method should throw this type of exception when no set method. + return; + } + + throw new InvalidOperationException($"Should throw {nameof(DeserializationException)}."); + } + } +} diff --git a/nanoFramework.Json.Test/Resolvers/MemberResolverCaseInsensitiveTests.cs b/nanoFramework.Json.Test/Resolvers/MemberResolverCaseInsensitiveTests.cs new file mode 100644 index 00000000..82599b43 --- /dev/null +++ b/nanoFramework.Json.Test/Resolvers/MemberResolverCaseInsensitiveTests.cs @@ -0,0 +1,74 @@ +// +// Copyright (c) .NET Foundation and Contributors +// Portions Copyright 2007 James Newton-King, (c) Pervasive Digital LLC +// See LICENSE file in the project root for full license information. +// + +using nanoFramework.Json.Configuration; +using nanoFramework.Json.Resolvers; +using nanoFramework.TestFramework; +using System; + +namespace nanoFramework.Json.Test.Resolvers +{ + [TestClass] + public class MemberResolverCaseInsensitiveTests + { + private class TestClass + { + public int TestField = 1; + public int TestProperty { get; set; } = 1; + } + + [Setup] + public void MemberResolverCaseInsensitiveTests_Setup() + { + Settings.CaseSensitive = false; + } + + [Cleanup] + public void MemberResolverCaseInsensitive_Cleanup() + { + Settings.CaseSensitive = true; + } + + [TestMethod] + public void MemberResolverCaseInsensitive_Get_ShouldReturnCaseInsensitivePropertyWhenSet() + { + var resolver = new MemberResolver(); + var classInstance = new TestClass(); + var valueToSet = 6; + + var member = resolver.Get(nameof(TestClass.TestProperty).ToLower(), typeof(TestClass)); + member.SetValue(classInstance, valueToSet); + + Assert.Equal(classInstance.TestProperty, valueToSet); + Assert.Equal(member.ObjectType.FullName, typeof(int).FullName); + } + + + [TestMethod] + public void MemberResolverCaseInsensitive_Get_ShouldReturnCaseInsensitiveFieldWhenSet() + { + var resolver = new MemberResolver(); + var classInstance = new TestClass(); + var valueToSet = 5; + + var member = resolver.Get(nameof(TestClass.TestField).ToLower(), typeof(TestClass)); + member.SetValue(classInstance, valueToSet); + + Assert.Equal(classInstance.TestField, valueToSet); + Assert.Equal(member.ObjectType.FullName, typeof(int).FullName); + } + + [TestMethod] + public void MemberResolverCaseInsensitive_Get_ShouldSkipWhenNotFoundCaseInsensitiveProperty() + { + var resolver = new MemberResolver(); + + var member = resolver.Get("NotExistingProperty", typeof(TestClass)); + + Assert.Equal(member.Skip, true); + } + } +} \ No newline at end of file diff --git a/nanoFramework.Json.Test/Resolvers/MemberResolverCaseSensitiveExceptionTests.cs b/nanoFramework.Json.Test/Resolvers/MemberResolverCaseSensitiveExceptionTests.cs new file mode 100644 index 00000000..82caff5d --- /dev/null +++ b/nanoFramework.Json.Test/Resolvers/MemberResolverCaseSensitiveExceptionTests.cs @@ -0,0 +1,65 @@ +using nanoFramework.Json.Configuration; +using nanoFramework.Json.Resolvers; +using nanoFramework.TestFramework; +using System; + +namespace nanoFramework.Json.Test.Resolvers +{ + [TestClass] + public class MemberResolverCaseSensitiveExceptionTests + { + private class TestClass + { + public int NoGetProperty { private get; set; } = 1; + public int NoSetProperty { get; } = 1; + } + + [Setup] + public void MemberResolverCaseSensitiveExceptionTests_Setup() + { + Settings.ThrowExceptionWhenPropertyNotFound = true; + } + + [Cleanup] + public void MemberResolverCaseSensitiveExceptionTests_Cleanup() + { + Settings.ThrowExceptionWhenPropertyNotFound = false; + } + + [TestMethod] + public void MemberResolverCaseSensitive_Get_ShouldThrowPropertyWithoutGetWhenSet() + { + var resolver = new MemberResolver(); + + try + { + resolver.Get(nameof(TestClass.NoGetProperty), typeof(TestClass)); + } + catch (DeserializationException) + { + // Intended. Method should throw this type of exception when no set method. + return; + } + + throw new InvalidOperationException($"Should throw {nameof(DeserializationException)}."); + } + + [TestMethod] + public void MemberResolverCaseSensitive_Get_ShouldThrowPropertyWithoutSetWhenSet() + { + var resolver = new MemberResolver(); + + try + { + resolver.Get(nameof(TestClass.NoSetProperty), typeof(TestClass)); + } + catch (DeserializationException) + { + // Intended. Method should throw this type of exception when no set method. + return; + } + + throw new InvalidOperationException($"Should throw {nameof(DeserializationException)}."); + } + } +} diff --git a/nanoFramework.Json.Test/Resolvers/MemberResolverCaseSensitiveTests.cs b/nanoFramework.Json.Test/Resolvers/MemberResolverCaseSensitiveTests.cs new file mode 100644 index 00000000..f3523b1d --- /dev/null +++ b/nanoFramework.Json.Test/Resolvers/MemberResolverCaseSensitiveTests.cs @@ -0,0 +1,71 @@ +// +// Copyright (c) .NET Foundation and Contributors +// Portions Copyright 2007 James Newton-King, (c) Pervasive Digital LLC +// See LICENSE file in the project root for full license information. +// + +using nanoFramework.Json.Resolvers; +using nanoFramework.TestFramework; + +namespace nanoFramework.Json.Test.Resolvers +{ + [TestClass] + public class MemberResolverCaseSensitiveTests + { + private class TestClass + { + public int TestField = 1; + public int TestProperty { get; set; } = 1; + public int NoGetProperty { private get; set; } = 1; + public int NoSetProperty { get; } = 1; + } + + [TestMethod] + public void MemberResolverCaseSensitive_Get_ShouldResolveField() + { + var resolver = new MemberResolver(); + var classInstance = new TestClass(); + var valueToSet = 5; + + var member = resolver.Get(nameof(TestClass.TestField), typeof(TestClass)); + member.SetValue(classInstance, valueToSet); + + Assert.Equal(classInstance.TestField, valueToSet); + Assert.Equal(member.ObjectType.FullName, typeof(int).FullName); + } + + [TestMethod] + public void MemberResolverCaseSensitive_Get_ShouldResolveProperty() + { + var resolver = new MemberResolver(); + var classInstance = new TestClass(); + var valueToSet = 6; + + var member = resolver.Get(nameof(TestClass.TestProperty), typeof(TestClass)); + member.SetValue(classInstance, valueToSet); + + Assert.Equal(classInstance.TestProperty, valueToSet); + Assert.Equal(member.ObjectType.FullName, typeof(int).FullName); + } + + [TestMethod] + public void MemberResolverCaseSensitive_Get_ShouldSkipPropertyWithoutGet() + { + var resolver = new MemberResolver(); + + var member = resolver.Get(nameof(TestClass.NoGetProperty), typeof(TestClass)); + + Assert.True(member.Skip); + } + + [TestMethod] + public void MemberResolverCaseSensitive_Get_ShouldSkipPropertyWithoutSet() + { + var resolver = new MemberResolver(); + + var member = resolver.Get(nameof(TestClass.NoSetProperty), typeof(TestClass)); + + Assert.True(member.Skip); + } + } +} diff --git a/nanoFramework.Json.Test/nanoFramework.Json.Test.nfproj b/nanoFramework.Json.Test/nanoFramework.Json.Test.nfproj index 9337471c..788e1946 100644 --- a/nanoFramework.Json.Test/nanoFramework.Json.Test.nfproj +++ b/nanoFramework.Json.Test/nanoFramework.Json.Test.nfproj @@ -32,7 +32,9 @@ - + + + @@ -46,9 +48,14 @@ - + + + + + + diff --git a/nanoFramework.Json/Converters/ConvertersMapping.cs b/nanoFramework.Json/Configuration/ConvertersMapping.cs similarity index 95% rename from nanoFramework.Json/Converters/ConvertersMapping.cs rename to nanoFramework.Json/Configuration/ConvertersMapping.cs index d2a57e5c..859b37ae 100644 --- a/nanoFramework.Json/Converters/ConvertersMapping.cs +++ b/nanoFramework.Json/Configuration/ConvertersMapping.cs @@ -1,7 +1,8 @@ -using System; +using nanoFramework.Json.Converters; +using System; using System.Collections; -namespace nanoFramework.Json.Converters +namespace nanoFramework.Json.Configuration { /// /// Contains all converters for JSON. diff --git a/nanoFramework.Json/Configuration/Settings.cs b/nanoFramework.Json/Configuration/Settings.cs new file mode 100644 index 00000000..22aa7240 --- /dev/null +++ b/nanoFramework.Json/Configuration/Settings.cs @@ -0,0 +1,32 @@ +// +// Copyright (c) .NET Foundation and Contributors +// Portions Copyright 2007 James Newton-King, (c) Pervasive Digital LLC +// See LICENSE file in the project root for full license information. +// + +using nanoFramework.Json.Resolvers; + +namespace nanoFramework.Json.Configuration +{ + /// + /// Common settings for JSON converter. + /// + public static class Settings + { + /// + /// Gets or sets resolver which is used to find properties in target object when deserializing JSON. + /// + public static IMemberResolver Resolver { get; set; } = new MemberResolver(); + + /// + /// Gets or sets a value indicating whether property resolving should be case sensitive. + /// + public static bool CaseSensitive { get; set; } = true; + + /// + /// Gets or sets a value indicating whether deserialization should throw exception when no property found. + /// + public static bool ThrowExceptionWhenPropertyNotFound { get; set; } = false; + } +} + diff --git a/nanoFramework.Json/Converters/BoolConverter.cs b/nanoFramework.Json/Converters/BoolConverter.cs index d62b2014..89ceccc5 100644 --- a/nanoFramework.Json/Converters/BoolConverter.cs +++ b/nanoFramework.Json/Converters/BoolConverter.cs @@ -18,7 +18,7 @@ public string ToJson(object value) /// public object ToType(object value) { - throw new NotImplementedException(); + return (bool)value; } } } \ No newline at end of file diff --git a/nanoFramework.Json/Converters/DateTimeConverter.cs b/nanoFramework.Json/Converters/DateTimeConverter.cs index b609d9ad..74e27f96 100644 --- a/nanoFramework.Json/Converters/DateTimeConverter.cs +++ b/nanoFramework.Json/Converters/DateTimeConverter.cs @@ -1,5 +1,10 @@ -using System; -using System.Text; +// +// Copyright (c) .NET Foundation and Contributors +// Portions Copyright 2007 James Newton-King, (c) Pervasive Digital LLC +// See LICENSE file in the project root for full license information. +// + +using System; namespace nanoFramework.Json.Converters { @@ -18,8 +23,7 @@ public string ToJson(object value) /// public object ToType(object value) { - // Not sure hwo this one should work - throw new NotImplementedException(); + return value; } } } diff --git a/nanoFramework.Json/Converters/DoubleConverter.cs b/nanoFramework.Json/Converters/DoubleConverter.cs index 869226e3..e215ef00 100644 --- a/nanoFramework.Json/Converters/DoubleConverter.cs +++ b/nanoFramework.Json/Converters/DoubleConverter.cs @@ -1,5 +1,10 @@ -using System; -using System.ComponentModel; +// +// Copyright (c) .NET Foundation and Contributors +// Portions Copyright 2007 James Newton-King, (c) Pervasive Digital LLC +// See LICENSE file in the project root for full license information. +// + +using System; namespace nanoFramework.Json.Converters { @@ -23,6 +28,11 @@ public string ToJson(object value) /// public object ToType(object value) { + if (value == null) + { + return double.NaN; + } + return Convert.ToDouble(value.ToString()); } } diff --git a/nanoFramework.Json/Converters/FloatConverter.cs b/nanoFramework.Json/Converters/FloatConverter.cs index 769f4a38..6bb71a66 100644 --- a/nanoFramework.Json/Converters/FloatConverter.cs +++ b/nanoFramework.Json/Converters/FloatConverter.cs @@ -1,4 +1,10 @@ -using System; +// +// Copyright (c) .NET Foundation and Contributors +// Portions Copyright 2007 James Newton-King, (c) Pervasive Digital LLC +// See LICENSE file in the project root for full license information. +// + +using System; namespace nanoFramework.Json.Converters { @@ -22,6 +28,11 @@ public string ToJson(object value) /// public object ToType(object value) { + if (value == null) + { + return float.NaN; + } + return Convert.ToSingle(value.ToString()); } } diff --git a/nanoFramework.Json/Converters/StringConverter.cs b/nanoFramework.Json/Converters/StringConverter.cs index f67787f5..5a8c270b 100644 --- a/nanoFramework.Json/Converters/StringConverter.cs +++ b/nanoFramework.Json/Converters/StringConverter.cs @@ -81,32 +81,38 @@ internal static bool StringContainsCharactersToEscape(string str, bool deseriali /// public object ToType(object value) { + if (value == null) + { + return string.Empty; + } + var sourceString = value.ToString(); + if (!StringContainsCharactersToEscape(sourceString, true)) + { + return value; + } + //String by default has escaped \" at beggining and end, just remove them var resultString = sourceString.Substring(1, sourceString.Length - 2); - if (StringContainsCharactersToEscape(resultString, true)) + var newString = new StringBuilder(); + //Last character can not be escaped, because it's last one + for (int i = 0; i < resultString.Length - 1; i++) { - var newString = new StringBuilder(); - //Last character can not be escaped, because it's last one - for (int i = 0; i < resultString.Length - 1; i++) + var curChar = resultString[i]; + var nextChar = resultString[i + 1]; + + if (curChar == '\\') { - var curChar = resultString[i]; - var nextChar = resultString[i + 1]; - - if (curChar == '\\') - { - var charToAppend = GetEscapableCharKeyBasedOnValue(nextChar); - newString.Append(charToAppend); - i++; - continue; - } - newString.Append(curChar); + var charToAppend = GetEscapableCharKeyBasedOnValue(nextChar); + newString.Append(charToAppend); + i++; + continue; } - //Append last character skkiped by loop - newString.Append(resultString[resultString.Length - 1]); - return newString.ToString(); + newString.Append(curChar); } - return resultString; + //Append last character skkiped by loop + newString.Append(resultString[resultString.Length - 1]); + return newString.ToString(); } private static char GetEscapableCharKeyBasedOnValue(char inputChar) diff --git a/nanoFramework.Json/Converters/TimeSpanConverter.cs b/nanoFramework.Json/Converters/TimeSpanConverter.cs index 1d635312..db8bfa7a 100644 --- a/nanoFramework.Json/Converters/TimeSpanConverter.cs +++ b/nanoFramework.Json/Converters/TimeSpanConverter.cs @@ -140,7 +140,7 @@ private static int ParseValueFromString(bool hasValue,string[] timeSpanBits, ref private static bool IsInvalidTimeSpan(int hour, int minutes, int seconds) { - if (hour < 0 || hour >= 24) + if (hour < 0 || hour > 24) { return true; } diff --git a/nanoFramework.Json/JsonConvert.cs b/nanoFramework.Json/JsonConvert.cs index 1b428daf..3e2afdbb 100644 --- a/nanoFramework.Json/JsonConvert.cs +++ b/nanoFramework.Json/JsonConvert.cs @@ -4,7 +4,7 @@ // See LICENSE file in the project root for full license information. // -using nanoFramework.Json; +using nanoFramework.Json.Configuration; using nanoFramework.Json.Converters; using System; using System.Collections; @@ -104,17 +104,31 @@ private static object Deserialize(StreamReader dr) } #endif - internal static object ConvertToType(Type sourceType, Type targetType, object value) + private static bool ShouldSkipConvert(Type sourceType, Type targetType, bool forceConversion) { - // No need to convert if values matches - if (sourceType == targetType) + if (forceConversion) + { + return false; + } + + if (sourceType != targetType) + { + return false; + } + + return true; + } + + internal static object ConvertToType(Type sourceType, Type targetType, object value, bool forceConversion = false) + { + if (ShouldSkipConvert(sourceType, targetType, forceConversion)) { return value; } if (targetType.IsArray) { - return ConvertToType(sourceType, targetType.GetElementType(), value); + return ConvertToType(sourceType, targetType.GetElementType(), value, forceConversion); } if (ConvertersMapping.ConversionTable.Contains(targetType)) @@ -254,41 +268,11 @@ private static object PopulateObject(JsonToken rootToken, Type rootType, string memberPropertyName = "_" + memberProperty.Name.Substring(1); } - // Figure out if we're dealing with a Field or a Property and handle accordingly - Type memberType = null; - FieldInfo memberFieldInfo = null; - MethodInfo memberPropSetMethod = null; - MethodInfo memberPropGetMethod = null; - bool memberIsProperty = false; - - memberFieldInfo = rootType.GetField(memberPropertyName); - - if (memberFieldInfo != null) + // Call current resolver to get info how to deal with data + var memberResolver = Settings.Resolver.Get(memberPropertyName, rootType); + if (memberResolver.Skip) { - memberType = memberFieldInfo.FieldType; - memberIsProperty = false; - } - else - { - memberPropGetMethod = rootType.GetMethod("get_" + memberPropertyName); - - if (memberPropGetMethod == null) - { - continue; - } - else - { - memberType = memberPropGetMethod.ReturnType; - memberPropSetMethod = rootType.GetMethod("set_" + memberPropertyName); - - if (memberType == null) - { - // failed to get setter of memberType {rootType.Name}. Possibly this property doesn't have a setter. - throw new DeserializationException(); - } - - memberIsProperty = true; - } + continue; } // Process the member based on JObject, JValue, or JArray @@ -311,7 +295,8 @@ private static object PopulateObject(JsonToken rootToken, Type rootType, string object memberObject = null; // check if property type it's HashTable - if (memberType.FullName == "System.Collections.Hashtable") + // whole if can be replaced with memberObject = PopulateObject(memberProperty.Value, memberType, memberPath);?? + if (memberResolver.ObjectType.FullName == "System.Collections.Hashtable") { Hashtable table = new(); @@ -335,87 +320,26 @@ private static object PopulateObject(JsonToken rootToken, Type rootType, string } else { - memberObject = PopulateObject(memberProperty.Value, memberType, memberPath); + memberObject = PopulateObject(memberProperty.Value, memberResolver.ObjectType, memberPath); } - if (memberIsProperty) - { - memberPropSetMethod.Invoke(rootInstance, new object[] { memberObject }); - } - else - { - memberFieldInfo.SetValue(rootInstance, memberObject); - } + memberResolver.SetValue(rootInstance, memberObject); } else if (memberProperty.Value is JsonValue memberPropertyValue) { - if (memberType != typeof(DateTime)) - { - if (memberPropertyValue.Value == null) - { - if (memberIsProperty) - { - if (!memberPropGetMethod.ReturnType.IsValueType) - { - memberPropSetMethod.Invoke(rootInstance, new object[] { null }); - } - else - { - switch (memberPropGetMethod.ReturnType.Name) - { - case "Single": - memberPropSetMethod.Invoke(rootInstance, new object[] { float.NaN }); - break; - - case "Double": - memberPropSetMethod.Invoke(rootInstance, new object[] { double.NaN }); - break; - } - - } - } - else - { - object obj = null; - memberFieldInfo.SetValue(rootInstance, obj); - } - } - else - { - if (memberIsProperty) - { - var convertedValueAsObject = ConvertToType(memberPropertyValue.Value.GetType(), - memberType, memberPropertyValue.Value); - memberPropSetMethod.Invoke(rootInstance, new object[] { convertedValueAsObject }); - } - else - { - memberFieldInfo.SetValue(rootInstance, memberPropertyValue.Value); - } - } - } - else - { - if (memberIsProperty) - { - memberPropSetMethod.Invoke(rootInstance, new object[] { memberPropertyValue.Value }); - } - else - { - memberFieldInfo.SetValue(rootInstance, memberPropertyValue.Value); - } - } + var returnType = memberPropertyValue.Value != null ? memberPropertyValue.Value.GetType() : memberResolver.ObjectType; + var convertedValueAsObject = ConvertToType(returnType, memberResolver.ObjectType, memberPropertyValue.Value, true); + memberResolver.SetValue(rootInstance, convertedValueAsObject); } else if (memberProperty.Value is JsonArray array) { // Need this type when we try to populate the array elements - Type memberElementType = memberType.GetElementType(); + Type memberElementType = memberResolver.ObjectType.GetElementType(); bool isArrayList = false; - if (memberElementType == null && memberType.FullName == "System.Collections.ArrayList") + if (memberElementType == null && memberResolver.ObjectType.FullName == "System.Collections.ArrayList") { - memberElementType = memberType; - + memberElementType = memberResolver.ObjectType; isArrayList = true; } @@ -432,14 +356,7 @@ private static object PopulateObject(JsonToken rootToken, Type rootType, string { if (item is JsonValue value) { - if (memberPropGetMethod == null) - { - // {rootType.Name} must have a valid Property Get Method - throw new DeserializationException(); - } - - var valueToAddAsObject = ConvertToType(value.Value.GetType(), - memberPropGetMethod.ReturnType, value.Value); + var valueToAddAsObject = ConvertToType(value.Value.GetType(), memberResolver.ObjectType, value.Value); memberValueArrayList.Add(valueToAddAsObject); } else if (item != null) @@ -485,14 +402,7 @@ private static object PopulateObject(JsonToken rootToken, Type rootType, string } // Populate rootInstance - if (memberIsProperty) - { - memberPropSetMethod.Invoke(rootInstance, new object[] { targetArray }); - } - else - { - memberFieldInfo.SetValue(rootInstance, targetArray); - } + memberResolver.SetValue(rootInstance, targetArray); } else { @@ -509,14 +419,7 @@ private static object PopulateObject(JsonToken rootToken, Type rootType, string memberValueArrayList.CopyTo(targetArray); // Populate rootInstance - if (memberIsProperty) - { - memberPropSetMethod.Invoke(rootInstance, new object[] { targetArray }); - } - else - { - memberFieldInfo.SetValue(rootInstance, targetArray); - } + memberResolver.SetValue(rootInstance, targetArray); } } } @@ -1033,9 +936,7 @@ private static JsonArray ParseArray(ref LexToken token) throw new DeserializationException(); } - var result = new JsonArray((JsonToken[])list.ToArray(typeof(JsonToken))); - - return result; + return new JsonArray((JsonToken[])list.ToArray(typeof(JsonToken))); } private static JsonToken ParseValue(ref LexToken token) @@ -1394,3 +1295,4 @@ private static LexToken EndToken(StringBuilder sb) private static bool IsNumberChar(char ch) => (ch == '-') || (ch == '+') || (ch == '.') || (ch == 'e') || (ch == 'E') || (ch >= '0' && ch <= '9'); } } + diff --git a/nanoFramework.Json/JsonSerializer.cs b/nanoFramework.Json/JsonSerializer.cs index 00ee00e6..780664ee 100644 --- a/nanoFramework.Json/JsonSerializer.cs +++ b/nanoFramework.Json/JsonSerializer.cs @@ -4,6 +4,7 @@ // See LICENSE file in the project root for full license information. // +using nanoFramework.Json.Configuration; using nanoFramework.Json.Converters; using System; using System.Collections; diff --git a/nanoFramework.Json/Resolvers/IMemberResolver.cs b/nanoFramework.Json/Resolvers/IMemberResolver.cs new file mode 100644 index 00000000..46de7105 --- /dev/null +++ b/nanoFramework.Json/Resolvers/IMemberResolver.cs @@ -0,0 +1,24 @@ +// +// Copyright (c) .NET Foundation and Contributors +// Portions Copyright 2007 James Newton-King, (c) Pervasive Digital LLC +// See LICENSE file in the project root for full license information. +// + +using System; + +namespace nanoFramework.Json.Resolvers +{ + /// + /// Interface for property resolvers. + /// + public interface IMemberResolver + { + /// + /// Gets the data about member from object which we want to populate. + /// + /// Key from JSON property. Property name we are looking for. + /// Type of object in which should be. + /// Data about member which we want to populate based on passed parameters. + MemberSet Get(string memberName, Type objectType); + } +} diff --git a/nanoFramework.Json/Resolvers/MemberResolver.cs b/nanoFramework.Json/Resolvers/MemberResolver.cs new file mode 100644 index 00000000..73140f52 --- /dev/null +++ b/nanoFramework.Json/Resolvers/MemberResolver.cs @@ -0,0 +1,111 @@ +// +// Copyright (c) .NET Foundation and Contributors +// Portions Copyright 2007 James Newton-King, (c) Pervasive Digital LLC +// See LICENSE file in the project root for full license information. +// + +using nanoFramework.Json.Configuration; +using System; +using System.Reflection; + +namespace nanoFramework.Json.Resolvers +{ + internal sealed class MemberResolver : IMemberResolver + { + public MemberSet Get(string memberName, Type objectType) + { + var memberFieldInfo = objectType.GetField(memberName); + + // Value will be set via field + if (memberFieldInfo != null) + { + return new MemberSet(new SetValueDelegate((instance, value) => memberFieldInfo.SetValue(instance, value)), memberFieldInfo.FieldType); + } + + var memberPropGetMethod = objectType.GetMethod("get_" + memberName); + if (memberPropGetMethod == null) + { + return HandleNullPropertyMember(memberName, objectType); + } + + var memberPropSetMethod = objectType.GetMethod("set_" + memberName); + if (memberPropSetMethod == null) + { + return HandleNullPropertyMember(memberName, objectType); + } + + return new MemberSet(new SetValueDelegate((instance, value) => memberPropSetMethod.Invoke(instance, new object[] { value })), memberPropGetMethod.ReturnType); + } + + private MemberSet HandleNullPropertyMember(string memberName, Type objectType) + { + if (!Settings.CaseSensitive) + { + return GetInsensitive(memberName, objectType); + } + + return HandlePropertyNotFound(); + } + + internal MemberSet GetInsensitive(string memberName, Type objectType) + { + var memberFieldInfo = GetFieldInfoCaseInsensitive(objectType, memberName); + + // Value will be set via field + if (memberFieldInfo != null) + { + return new MemberSet(new SetValueDelegate((instance, value) => memberFieldInfo.SetValue(instance, value)), memberFieldInfo.FieldType); + } + + var memberPropGetMethod = GetMethodCaseInsensitive(objectType, "get_" + memberName); + if (memberPropGetMethod == null) + { + return HandlePropertyNotFound(); + } + + var memberPropSetMethod = GetMethodCaseInsensitive(objectType, "set_" + memberName); + if (memberPropSetMethod == null) + { + return HandlePropertyNotFound(); + } + + return new MemberSet(new SetValueDelegate((instance, value) => memberPropSetMethod.Invoke(instance, new object[] { value })), memberPropGetMethod.ReturnType); + } + + private MemberSet HandlePropertyNotFound() + { + if (Settings.ThrowExceptionWhenPropertyNotFound) + { + throw new DeserializationException(); + } + + return new MemberSet(true); + } + + private static FieldInfo GetFieldInfoCaseInsensitive(Type objectType, string fieldName) + { + foreach (var field in objectType.GetFields()) + { + if (field.Name.ToLower() == fieldName.ToLower()) + { + return field; + } + } + + return null; + } + + private static MethodInfo GetMethodCaseInsensitive(Type objectType, string methodName) + { + foreach (var method in objectType.GetMethods()) + { + if (method.Name.ToLower() == methodName.ToLower()) + { + return method; + } + } + + return null; + } + } +} diff --git a/nanoFramework.Json/Resolvers/MemberSet.cs b/nanoFramework.Json/Resolvers/MemberSet.cs new file mode 100644 index 00000000..451f9b5f --- /dev/null +++ b/nanoFramework.Json/Resolvers/MemberSet.cs @@ -0,0 +1,61 @@ +// +// Copyright (c) .NET Foundation and Contributors +// Portions Copyright 2007 James Newton-King, (c) Pervasive Digital LLC +// See LICENSE file in the project root for full license information. +// + +using System; + +namespace nanoFramework.Json.Resolvers +{ + /// + /// A delegate which will be used for setting value on object. + /// + /// Object instance which will be used to set value on. + /// Value which we want to set. + public delegate void SetValueDelegate(object objectInstance, object valueToSet); + + /// + /// Data about member which we want to populate. + /// + public struct MemberSet + { + /// + /// Gets a delegate which tells how to set a value on object. + /// + public SetValueDelegate SetValue { get; } + + /// + /// Type of object we are trying to set value on. + /// + public Type ObjectType { get; } + + /// + /// Gets a value indicating whether current member should be skipped + /// + public bool Skip { get; } + + /// + /// Initialize new instance of MemberSet struct. + /// + /// Deletage which tells how to set a value on object. + /// Type of object we are trying to set value on. + public MemberSet(SetValueDelegate setValue, Type objectType) + { + SetValue = setValue; + ObjectType = objectType; + Skip = false; + } + + /// + /// Initialize new instance of MemberSet struct. + /// + /// Should skip current method. + public MemberSet(bool skip) + { + Skip = skip; + ObjectType = null; + SetValue = null; + } + } +} diff --git a/nanoFramework.Json/nanoFramework.Json.nfproj b/nanoFramework.Json/nanoFramework.Json.nfproj index 4fd2aec2..4c64f9bc 100644 --- a/nanoFramework.Json/nanoFramework.Json.nfproj +++ b/nanoFramework.Json/nanoFramework.Json.nfproj @@ -31,10 +31,11 @@ + - + @@ -51,6 +52,9 @@ + + +