Skip to content

Allow deserialization when properties case doesn't match #251

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 22 commits into from
Oct 20, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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).
Expand Down
Original file line number Diff line number Diff line change
@@ -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
Expand Down
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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));
}
Expand Down
23 changes: 23 additions & 0 deletions nanoFramework.Json.Test/Configuration/SettingsTests.cs
Original file line number Diff line number Diff line change
@@ -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);
}
}
}
10 changes: 5 additions & 5 deletions nanoFramework.Json.Test/Converters/BoolConverterTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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")]
Expand Down
20 changes: 20 additions & 0 deletions nanoFramework.Json.Test/Converters/DateTimeConverterTests.cs
Original file line number Diff line number Diff line change
@@ -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);
}
}
}
9 changes: 9 additions & 0 deletions nanoFramework.Json.Test/Converters/DoubleConverterTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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")]
Expand Down
9 changes: 9 additions & 0 deletions nanoFramework.Json.Test/Converters/FloatConverterTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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")]
Expand Down
14 changes: 12 additions & 2 deletions nanoFramework.Json.Test/Converters/StringConverterTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand All @@ -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();
Expand Down
2 changes: 2 additions & 0 deletions nanoFramework.Json.Test/Converters/TimeSpanConverterTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand All @@ -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();
Expand Down
37 changes: 37 additions & 0 deletions nanoFramework.Json.Test/JsonDeserializationArraysTests.cs
Original file line number Diff line number Diff line change
@@ -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);
}
}
}
40 changes: 2 additions & 38 deletions nanoFramework.Json.Test/JsonUnitTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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()
{
Expand Down Expand Up @@ -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]
Expand Down
Original file line number Diff line number Diff line change
@@ -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)}.");
}
}
}
Loading