Skip to content

Commit

Permalink
Add support for HashSet serialization
Browse files Browse the repository at this point in the history
The code has been updated to allow for `HashSet<T>` serialization. The JsonUtils class was modified to handle the HashSet instances and the corresponding test cases have been added in JsonUtilsTests.
  • Loading branch information
kivikko committed Mar 8, 2024
1 parent a74433c commit e2529f5
Show file tree
Hide file tree
Showing 6 changed files with 44 additions and 24 deletions.
4 changes: 3 additions & 1 deletion Kivikko.Json.Tests/JsonUtilsTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ public static IEnumerable<TestCaseData> FromJsonCases()
yield return new TestCaseData(typeof(IEnumerable<int>), "[1,2]").Returns(new[] { 1, 2 });
yield return new TestCaseData(typeof(List<int>), "[1,2]").Returns(new List<int> { 1, 2 });
yield return new TestCaseData(typeof(List<List<int>>), "[[1,2],[3,4]]").Returns(new List<List<int>> { new() { 1, 2 }, new() { 3, 4} });
yield return new TestCaseData(typeof(HashSet<int>), "[1,2]").Returns(new HashSet<int> { 1, 2 });
yield return new TestCaseData(typeof(int[][]), "[[1,2],[3,4]]").Returns(new[] { new[] { 1, 2 }, new[] { 3, 4 } });
yield return new TestCaseData(typeof((string, int)), "{\"Item1\":\"A\",\"Item2\":1}").Returns(("A", 1));
yield return new TestCaseData(typeof(Dictionary<int, int>), "{\"1\":2,\"2\":3}").Returns(new Dictionary<int, int> { [1] = 2, [2] = 3 });
Expand All @@ -124,7 +125,7 @@ public static IEnumerable<TestCaseData> FromJsonCases()
yield return new TestCaseData(typeof(Dictionary<int, Dictionary<int, int>>), "{\"1\":{\"2\":3,\"3\":4},\"2\":{\"3\":4}}").Returns(new Dictionary<int, Dictionary<int, int>> { [1] = new() { [2] = 3, [3] = 4 }, [2] = new() { [3] = 4 } });
yield return new TestCaseData(typeof(NestedClass), "{Enum:1,Integer:5}").Returns(new NestedClass { Enum = TestEnum.Value3, Integer = 5});
yield return new TestCaseData(typeof(NestedClass), "{\"Enum\":1,\"Integer\":5}").Returns(new NestedClass { Enum = TestEnum.Value3, Integer = 5});
yield return new TestCaseData(typeof(TestClass), "{\"NestedClass\":{\"Bool\":true,\"Double\":3.141592653589793,\"Integer\":5,\"DateTime\":\"2024-01-11T14:15:16+05:00\",\"Time\":\"1.02:03:04.0050060\",\"StringArray\":[\"A\",\"B\",\"C\"],\"StringEnumerable\":[\"K\",\"L\",\"M\"],\"StringList\":[\"X\",\"Y\",\"Z\"],\"Dictionary\":{\"1\":\"One\",\"2\":\"Two\",\"3\":\"Three\"}},\"Classes\":[{},{\"Enum\":-1},{\"Integer\":0},{\"Integer\":1,\"Enum\":1}],\"ClassesDictionary\":{\"0\":{\"Integer\":0},\"1\":{\"Integer\":1},\"2\":{\"Integer\":2}},\"String\":\"Hello\"}").Returns(
yield return new TestCaseData(typeof(TestClass), "{\"NestedClass\":{\"Bool\":true,\"Double\":3.141592653589793,\"Integer\":5,\"DateTime\":\"2024-01-11T14:15:16+05:00\",\"Time\":\"1.02:03:04.0050060\",\"HashSet\":[1,2,3],\"StringArray\":[\"A\",\"B\",\"C\"],\"StringEnumerable\":[\"K\",\"L\",\"M\"],\"StringList\":[\"X\",\"Y\",\"Z\"],\"Dictionary\":{\"1\":\"One\",\"2\":\"Two\",\"3\":\"Three\"}},\"Classes\":[{},{\"Enum\":-1},{\"Integer\":0},{\"Integer\":1,\"Enum\":1}],\"ClassesDictionary\":{\"0\":{\"Integer\":0},\"1\":{\"Integer\":1},\"2\":{\"Integer\":2}},\"String\":\"Hello\"}").Returns(
new TestClass
{
String = "Hello",
Expand All @@ -135,6 +136,7 @@ public static IEnumerable<TestCaseData> FromJsonCases()
Integer = 5,
DateTime = new DateTime(2024, 01, 11, 14, 15, 16, DateTimeKind.Local),
Time = new TimeSpan(days: 1, hours: 2, minutes: 3, seconds: 4, milliseconds: 5, microseconds: 6),
HashSet = new HashSet<int> { 1, 2, 3 },
StringArray = new[] { "A", "B", "C" },
StringEnumerable = new[] { "K", "L", "M" },
StringList = new List<string> { "X", "Y", "Z" },
Expand Down
1 change: 1 addition & 0 deletions Kivikko.Json.Tests/Models/Model1/NestedClass.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ public class NestedClass
public TestEnum Enum { get; set; }
public DateTime? DateTime { get; set; }
public TimeSpan? Time { get; set; }
public HashSet<int>? HashSet { get; set; }
public string[]? StringArray { get; set; }
public IEnumerable<string>? StringEnumerable { get; set; }
public List<string>? StringList { get; set; }
Expand Down
56 changes: 36 additions & 20 deletions Kivikko.Json/JsonUtils.cs
Original file line number Diff line number Diff line change
Expand Up @@ -200,28 +200,44 @@ private object GetObjectFromJson(string json, Type type)
setValue(typedArray);
}

else if (
propertyType.IsGenericType &&
propertyType.GetInterfaces().Any(i => i == typeof(IDictionary)) &&
value is IDictionary valueDictionary)
else if (propertyType.IsGenericType)
{
var genericArgs = propertyType.GetGenericArguments();
var keyType = genericArgs[0];
var valueType = genericArgs[1];
var typedDictionary = (IDictionary)Activator.CreateInstance(typeof(Dictionary<,>).MakeGenericType(keyType, valueType));
foreach (DictionaryEntry entry in valueDictionary) typedDictionary.Add(entry.Key, entry.Value);
setValue(typedDictionary);
}
var interfaces = propertyType.GetInterfaces();

if (interfaces.Any(i => i == typeof(IDictionary)) &&
value is IDictionary valueDictionary)
{
var genericArgs = propertyType.GetGenericArguments();
var keyType = genericArgs[0];
var valueType = genericArgs[1];
var typedDictionary = (IDictionary)Activator.CreateInstance(typeof(Dictionary<,>).MakeGenericType(keyType, valueType));
foreach (DictionaryEntry entry in valueDictionary) typedDictionary.Add(entry.Key, entry.Value);
setValue(typedDictionary);
}

else if (
propertyType.IsGenericType &&
propertyType.GetInterfaces().Any(i => i == typeof(IEnumerable)) &&
propertyType.GetGenericArguments()[0] is { } listElementType &&
value is ICollection valueCollection)
{
var typedList = (IList)Activator.CreateInstance(typeof(List<>).MakeGenericType(listElementType));
foreach (var v in valueCollection) typedList.Add(v);
setValue(typedList);
else if (
interfaces.Any(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(ISet<>)) &&
propertyType.GetGenericArguments()[0] is { } setElementType &&
value is ICollection setValueCollection)
{
var typedSet = Activator.CreateInstance(typeof(HashSet<>).MakeGenericType(setElementType), setValueCollection);
setValue(typedSet);
}

else if (
interfaces.Any(i => i == typeof(IEnumerable)) &&
propertyType.GetGenericArguments()[0] is { } listElementType &&
value is ICollection valueCollection)
{
var typedList = (IList)Activator.CreateInstance(typeof(List<>).MakeGenericType(listElementType));
foreach (var v in valueCollection) typedList.Add(v);
setValue(typedList);
}

else
{
setValue(value);
}
}

else
Expand Down
2 changes: 1 addition & 1 deletion Kivikko.Json/Kivikko.Json.nuspec
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<package >
<metadata>
<id>Kivikko.Json</id>
<version>1.0.8</version>
<version>1.0.9</version>
<title>Kivikko.Json</title>
<authors>Sergei Petrov</authors>
<requireLicenseAcceptance>false</requireLicenseAcceptance>
Expand Down
4 changes: 2 additions & 2 deletions Kivikko.Json/Properties/AssemblyInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,5 +31,5 @@
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.8.0")]
[assembly: AssemblyFileVersion("1.0.8.0")]
[assembly: AssemblyVersion("1.0.9.0")]
[assembly: AssemblyFileVersion("1.0.9.0")]
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ The `JsonUtils` supports the following .NET types for serialization and deserial
- Tuples (for instance: `(T1,T2)`)
- Collections implementing `IEnumerable` interface (for instance: `List<T>`, `T[]`, etc.)
- Dictionaries implementing `IDictionary` interface (for instance: `Dictionary<TKey, TValue>`)
- `HashSet<T>`
- Any custom user types (`class`, `struct`) with public properties and/or fields

Please, be aware that the library will attempt to serialize public properties and fields of your custom types.
Expand Down

0 comments on commit e2529f5

Please sign in to comment.