Skip to content
This repository has been archived by the owner on Jan 23, 2023. It is now read-only.
/ corefx Public archive

Commit

Permalink
Add support for generic interface-based collections in JsonSerializer (
Browse files Browse the repository at this point in the history
…#36756)

* Add support for generic interface-based collections in JsonSerializer

Specifically, add enumerable converters for:

* IEnumerable<T>
* ICollection<T>
* IList<T>
* IReadOnlyCollection<T>
* IReadOnlyList<T>

This partially addresses https://github.com/dotnet/corefx/issues/36643

* Address review comments

* Add serialization tests for generic interface collection as members of class objects
  • Loading branch information
layomia authored Apr 13, 2019
1 parent 07f443b commit cc01ec9
Show file tree
Hide file tree
Showing 13 changed files with 1,432 additions and 11 deletions.
1 change: 1 addition & 0 deletions src/System.Text.Json/src/System.Text.Json.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
<Compile Include="System\Text\Json\Serialization\Converters\DefaultArrayConverter.cs" />
<Compile Include="System\Text\Json\Serialization\Converters\DefaultConverters.cs" />
<Compile Include="System\Text\Json\Serialization\Converters\DefaultConvertersOfTValue.cs" />
<Compile Include="System\Text\Json\Serialization\Converters\DefaultEnumerableConverter.cs" />
<Compile Include="System\Text\Json\Serialization\Converters\JsonValueConverterBoolean.cs" />
<Compile Include="System\Text\Json\Serialization\Converters\JsonValueConverterByte.cs" />
<Compile Include="System\Text\Json\Serialization\Converters\JsonValueConverterChar.cs" />
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
using System.Collections;
using System.Collections.Generic;
using System.Text.Json.Serialization.Policies;

namespace System.Text.Json.Serialization.Converters
{
internal class JsonEnumerableT<T> : ICollection<T>, IEnumerable<T>, IList<T>, IReadOnlyCollection<T>, IReadOnlyList<T>
{
List<T> _list;

public JsonEnumerableT(IList sourceList)
{
// TODO: Change sourceList from IList to List<T> so we can do a direct assignment here.
_list = new List<T>();

foreach (object item in sourceList)
{
if (item is T itemT)
{
_list.Add(itemT);
}
}
}

public T this[int index] { get => (T)_list[index]; set => _list[index] = value; }

public int Count => _list.Count;

public bool IsReadOnly => false;

public void Add(T item)
{
_list.Add(item);
}

public void Clear()
{
_list.Clear();
}

public bool Contains(T item)
{
return _list.Contains(item);
}

public void CopyTo(T[] array, int arrayIndex)
{
_list.CopyTo(array, arrayIndex);
}

public IEnumerator<T> GetEnumerator()
{
return _list.GetEnumerator();
}

public int IndexOf(T item)
{
return _list.IndexOf(item);
}

public void Insert(int index, T item)
{
_list.Insert(index, item);
}

public bool Remove(T item)
{
return _list.Remove(item);
}

public void RemoveAt(int index)
{
_list.RemoveAt(index);
}

IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}

internal sealed class DefaultEnumerableConverter : JsonEnumerableConverter
{
public override IEnumerable CreateFromList(Type elementType, IList sourceList)
{
Type t = typeof(JsonEnumerableT<>).MakeGenericType(elementType);
return (IEnumerable)Activator.CreateInstance(t, sourceList);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -275,7 +275,7 @@ private static ulong GetKey(ReadOnlySpan<byte> propertyName)

}

private static Type GetElementType(Type propertyType)
public static Type GetElementType(Type propertyType)
{
Type elementType = null;
if (typeof(IEnumerable).IsAssignableFrom(propertyType))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using System.Collections;
using System.Reflection;
using System.Text.Json.Serialization.Converters;
using System.Text.Json.Serialization.Policies;
Expand All @@ -10,9 +11,6 @@ namespace System.Text.Json.Serialization
{
internal abstract class JsonPropertyInfo
{
// For now, just a global converter.
private static JsonEnumerableConverter s_jsonEnumerableConverter = new DefaultArrayConverter();

internal ClassType ClassType;

internal byte[] _name = default;
Expand Down Expand Up @@ -70,7 +68,16 @@ internal virtual void GetPolicies(JsonSerializerOptions options)
{
if (RuntimePropertyType.IsArray)
{
EnumerableConverter = s_jsonEnumerableConverter;
EnumerableConverter = new DefaultArrayConverter();
}
else if (typeof(IEnumerable).IsAssignableFrom(RuntimePropertyType))
{
Type elementType = JsonClassInfo.GetElementType(RuntimePropertyType);

if (RuntimePropertyType.IsAssignableFrom(typeof(JsonEnumerableT<>).MakeGenericType(elementType)))
{
EnumerableConverter = new DefaultEnumerableConverter();
}
}
}

Expand Down
70 changes: 70 additions & 0 deletions src/System.Text.Json/tests/Serialization/Array.ReadTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,5 +35,75 @@ public static void ReadClassWithGenericList()
TestClassWithGenericList obj = JsonSerializer.Parse<TestClassWithGenericList>(TestClassWithGenericList.s_data);
obj.Verify();
}

[Fact]
public static void ReadClassWithObjectIEnumerableT()
{
TestClassWithObjectIEnumerableT obj = JsonSerializer.Parse<TestClassWithObjectIEnumerableT>(TestClassWithObjectIEnumerableT.s_data);
obj.Verify();
}

[Fact]
public static void ReadClassWithObjectIListT()
{
TestClassWithObjectIListT obj = JsonSerializer.Parse<TestClassWithObjectIListT>(TestClassWithObjectIListT.s_data);
obj.Verify();
}

[Fact]
public static void ReadClassWithObjectICollectionT()
{
TestClassWithObjectICollectionT obj = JsonSerializer.Parse<TestClassWithObjectICollectionT>(TestClassWithObjectICollectionT.s_data);
obj.Verify();
}

[Fact]
public static void ReadClassWithObjectIReadOnlyCollectionT()
{
TestClassWithObjectIReadOnlyCollectionT obj = JsonSerializer.Parse<TestClassWithObjectIReadOnlyCollectionT>(TestClassWithObjectIReadOnlyCollectionT.s_data);
obj.Verify();
}

[Fact]
public static void ReadClassWithObjectIReadOnlyListT()
{
TestClassWithObjectIReadOnlyListT obj = JsonSerializer.Parse<TestClassWithObjectIReadOnlyListT>(TestClassWithObjectIReadOnlyListT.s_data);
obj.Verify();
}

[Fact]
public static void ReadClassWithGenericIEnumerableT()
{
TestClassWithGenericIEnumerableT obj = JsonSerializer.Parse<TestClassWithGenericIEnumerableT>(TestClassWithGenericIEnumerableT.s_data);
obj.Verify();
}

[Fact]
public static void ReadClassWithGenericIListT()
{
TestClassWithGenericIListT obj = JsonSerializer.Parse<TestClassWithGenericIListT>(TestClassWithGenericIListT.s_data);
obj.Verify();
}

[Fact]
public static void ReadClassWithGenericICollectionT()
{
TestClassWithGenericICollectionT obj = JsonSerializer.Parse<TestClassWithGenericICollectionT>(TestClassWithGenericICollectionT.s_data);
obj.Verify();
}

[Fact]
public static void ReadClassWithGenericIReadOnlyCollectionT()
{
TestClassWithGenericIReadOnlyCollectionT obj = JsonSerializer.Parse<TestClassWithGenericIReadOnlyCollectionT>(TestClassWithGenericIReadOnlyCollectionT.s_data);
obj.Verify();
}

[Fact]
public static void ReadClassWithGenericIReadOnlyListT()
{
TestClassWithGenericIReadOnlyListT obj = JsonSerializer.Parse<TestClassWithGenericIReadOnlyListT>(TestClassWithGenericIReadOnlyListT.s_data);
obj.Verify();
}
}
}
Loading

0 comments on commit cc01ec9

Please sign in to comment.