Skip to content
This repository was archived by the owner on Jan 23, 2023. It is now read-only.
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
17af6d0
Added support for deserializing into concrete ReadOnlyDictionary typ…
Aug 28, 2019
5ebae7a
Refactored array deserialization to support more concrete types (Coll…
Aug 29, 2019
725a871
Merging in latest from master.
Aug 29, 2019
3fffaec
Merging latest from master.
Aug 29, 2019
31fde32
Updated test cases which now pass. Added back in explicit type defini…
Aug 29, 2019
9022c5a
Performance improvement: Allow more types to be built up off of the f…
Aug 29, 2019
1793bea
Merge branch 'master' into ConcreteReadOnlyTypeDeserialization
Aug 30, 2019
015d26b
Initial cleanup.
Sep 2, 2019
37a622d
Refactor work in progress.
Sep 4, 2019
46d420d
Merge branch 'master' into ConcreteReadOnlyTypeDeserialization
Sep 4, 2019
063d2a6
Code review feedback.
Sep 4, 2019
cd71281
Merging latest from base branch.
Sep 4, 2019
0d009a5
More work in progress.
Sep 7, 2019
cce6a63
Merging latest from master.
Sep 7, 2019
4428de5
Merging latest from parent branch.
Sep 7, 2019
3d9e8bb
First sort of working version of Arrays.
Sep 8, 2019
9ab86b2
Added back in dictionary support.
Sep 9, 2019
1fa8145
Merge branch 'master' into ConcreteReadOnlyTypeDeserialization
Sep 9, 2019
a4def71
Merge branch 'ConcreteReadOnlyTypeDeserialization' into Deserializati…
Sep 9, 2019
bbf9806
Merging latest from master.
Sep 11, 2019
c7bf61e
Merging latest from parent branch.
Sep 11, 2019
4d0e57d
Fixes so far to get all the tests passing.
Sep 14, 2019
7199d34
Merging latest from master.
Sep 14, 2019
5b16bb5
Merging latest from parent branch.
Sep 14, 2019
2676ad8
Fixes to get tests passing.
Sep 23, 2019
586901b
Merging latest from master.
Sep 23, 2019
99b3dc7
Merging latest from parent branch.
Sep 23, 2019
b201dbe
More test fixup.
Sep 23, 2019
58f6fec
Test fixup.
Sep 23, 2019
423ef21
Test fixup.
Sep 23, 2019
c41de95
Test fixup.
Sep 23, 2019
074ee95
Merge branch 'master' into ConcreteReadOnlyTypeDeserialization
Sep 25, 2019
69c1a89
Merge branch 'ConcreteReadOnlyTypeDeserialization' into Deserializati…
Sep 25, 2019
2de3759
Trying to fix PolymorphicProperty path.
Sep 25, 2019
a0be9bb
Working on PolymorphicProperty path.
Sep 25, 2019
acb1e5d
PolymorphicProperty tests passing again.
Sep 26, 2019
fb557fc
Tests are finally passing.
Sep 26, 2019
558202f
Merge pull request #1 from CodeBlanch/DeserializationRefactoring
CodeBlanch Sep 26, 2019
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
10 changes: 9 additions & 1 deletion src/System.Text.Json/src/ILLinkTrim.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<!-- Instantiated via reflection -->
<method name=".ctor" />
</type>
<type fullname="System.Text.Json.Serialization.JsonPropertyInfoNotNullableContravariant`4">
<type fullname="System.Text.Json.Serialization.JsonPropertyInfoNotNullableContravariant`3">
<!-- Instantiated via reflection -->
<method name=".ctor" />
</type>
Expand All @@ -16,5 +16,13 @@
<!-- Instantiated via reflection -->
<method name=".ctor" />
</type>
<type fullname="System.Text.Json.Serialization.Converters.JsonEnumerableConverterState/CollectionBuilder`1">
<!-- Instantiated via reflection -->
<method name=".ctor" />
</type>
<type fullname="System.Text.Json.Serialization.Converters.JsonDictionaryConverterState/DictionaryBuilder`1">
<!-- Instantiated via reflection -->
<method name=".ctor" />
</type>
</assembly>
</linker>
3 changes: 3 additions & 0 deletions src/System.Text.Json/src/Resources/Strings.resx
Original file line number Diff line number Diff line change
Expand Up @@ -450,4 +450,7 @@
<data name="NotNodeJsonElementParent" xml:space="preserve">
<value>This JsonElement instance was not built from JsonNode</value>
</data>
<data name="DeserializeInstanceConstructorOfTypeNotFound" xml:space="preserve">
<value>Type '{0}' does not expose a public constructor that accepts items of '{1}' Type.</value>
</data>
</root>
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,5 @@ internal enum ClassType
Enumerable = 3,
// IDictionary
Dictionary = 4,
// Is deserialized by passing a IDictionary to its constructor
// i.e. immutable dictionaries, Hashtable, SortedList,
IDictionaryConstructible = 5,
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,38 @@
// See the LICENSE file in the project root for more information.

using System.Collections;
using System.Diagnostics;

namespace System.Text.Json.Serialization.Converters
{
internal sealed class DefaultArrayConverter : JsonEnumerableConverter
internal sealed class DefaultArrayConverter : JsonTemporaryListConverter
{
public override IEnumerable CreateFromList(ref ReadStack state, IList sourceList, JsonSerializerOptions options)
public override bool OwnsImplementedCollectionType(Type declaredPropertyType, Type implementedCollectionType, Type collectionElementType)
{
Type elementType = state.Current.GetElementType();
return implementedCollectionType.IsArray;
}

public override Type ResolveRunTimeType(JsonPropertyInfo jsonPropertyInfo)
{
Debug.Assert(jsonPropertyInfo.RuntimePropertyType.IsArray);

return jsonPropertyInfo.RuntimePropertyType;
}

public override object EndEnumerable(ref ReadStack state, JsonSerializerOptions options)
{
Debug.Assert(state.Current.EnumerableConverterState?.TemporaryList != null);

JsonEnumerableConverterState converterState = state.Current.EnumerableConverterState;

Array array;

if (sourceList.Count > 0 && sourceList[0] is Array probe)
if (converterState.TemporaryList.Count > 0 && converterState.TemporaryList[0] is Array probe)
{
array = Array.CreateInstance(probe.GetType(), sourceList.Count);
array = Array.CreateInstance(probe.GetType(), converterState.TemporaryList.Count);

int i = 0;
foreach (IList child in sourceList)
foreach (IList child in converterState.TemporaryList)
{
if (child is Array childArray)
{
Expand All @@ -29,8 +44,8 @@ public override IEnumerable CreateFromList(ref ReadStack state, IList sourceList
}
else
{
array = Array.CreateInstance(elementType, sourceList.Count);
sourceList.CopyTo(array, 0);
array = Array.CreateInstance(state.Current.JsonPropertyInfo.CollectionElementType, converterState.TemporaryList.Count);
converterState.TemporaryList.CopyTo(array, 0);
}

return array;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,122 @@
// See the LICENSE file in the project root for more information.

using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;

namespace System.Text.Json.Serialization.Converters
{
internal sealed class DefaultDerivedDictionaryConverter : JsonDictionaryConverter
{
public override object CreateFromDictionary(ref ReadStack state, IDictionary sourceDictionary, JsonSerializerOptions options)
// Cache constructors for performance.
private static readonly ConcurrentDictionary<string, JsonClassInfo.ConstructorDelegate> s_ctors = new ConcurrentDictionary<string, JsonClassInfo.ConstructorDelegate>();
private static readonly ConcurrentDictionary<string, JsonDictionaryConverterState.DictionaryBuilderConstructorDelegate> s_dictionaryBuilderCtors = new ConcurrentDictionary<string, JsonDictionaryConverterState.DictionaryBuilderConstructorDelegate>();

public override bool OwnsImplementedCollectionType(Type declaredPropertyType, Type implementedCollectionType, Type collectionElementType)
{
return typeof(IDictionary).IsAssignableFrom(implementedCollectionType) ||
(implementedCollectionType.IsGenericType && typeof(IDictionary<,>).MakeGenericType(typeof(string), collectionElementType).IsAssignableFrom(implementedCollectionType));
}

public override Type ResolveRunTimeType(JsonPropertyInfo jsonPropertyInfo)
{
if (jsonPropertyInfo.DeclaredPropertyType.IsInterface)
{
return typeof(Dictionary<,>).MakeGenericType(typeof(string), jsonPropertyInfo.CollectionElementType);
}

return jsonPropertyInfo.RuntimePropertyType;
}

public override void BeginDictionary(ref ReadStack state, JsonSerializerOptions options)
{
Debug.Assert(state.Current.DictionaryConverterState == null);

object instance = CreateConcreteInstance(ref state, options);

if (instance is IDictionary dictionary)
{
state.Current.DictionaryConverterState = new JsonDictionaryConverterState
{
FinalDictionary = dictionary
};
}
else
{
Type dictionaryType = typeof(JsonDictionaryConverterState.DictionaryBuilder<>).MakeGenericType(state.Current.JsonPropertyInfo.CollectionElementType);

state.Current.DictionaryConverterState = new JsonDictionaryConverterState
{
Builder = CreateDictionaryBuilderInstance(dictionaryType, instance, options)
};
}
}

public override void AddItemToDictionary<T>(ref Utf8JsonReader reader, ref ReadStack state, JsonSerializerOptions options, string key, ref T value)
{
Debug.Assert(state.Current.DictionaryConverterState?.FinalDictionary != null ||
state.Current.DictionaryConverterState.Builder != null);

IDictionary finalDictionary = state.Current.DictionaryConverterState.FinalDictionary;

if (finalDictionary != null)
{
if (finalDictionary is IDictionary<string, T> typedDictionary)
{
typedDictionary[key] = value;
}
else
{
finalDictionary[key] = value;
}
}
else
{
state.Current.DictionaryConverterState.Builder.Add(key, ref value);
}
}

public override object EndDictionary(ref ReadStack state, JsonSerializerOptions options)
{
Debug.Assert(state.Current.DictionaryConverterState?.FinalDictionary != null ||
state.Current.DictionaryConverterState.Builder != null);

return state.Current.DictionaryConverterState.FinalDictionary ?? state.Current.DictionaryConverterState.Builder.Instance;
}

private object CreateConcreteInstance(ref ReadStack state, JsonSerializerOptions options)
{
if (state.Current.JsonPropertyInfo.DeclaredPropertyType.IsInterface)
{
JsonClassInfo.ConstructorDelegate ctor = FindCachedCtor(state.Current.JsonPropertyInfo.RuntimePropertyType, options);
if (ctor == null)
{
ThrowHelper.ThrowInvalidOperationException_DeserializePolymorphicInterface(state.Current.JsonPropertyInfo.RuntimePropertyType);
}
return ctor();
}
else
{
return state.Current.JsonPropertyInfo.DeclaredClassInfo.CreateObject();
}
}

private JsonDictionaryConverterState.DictionaryBuilder CreateDictionaryBuilderInstance(Type dictionaryType, object instance, JsonSerializerOptions options)
{
JsonDictionaryConverterState.DictionaryBuilderConstructorDelegate ctor = FindCachedDictionaryBuilderCtor(dictionaryType, options);
Debug.Assert(ctor != null);
return ctor(instance);
}

private JsonClassInfo.ConstructorDelegate FindCachedCtor(Type type, JsonSerializerOptions options)
{
return s_ctors.GetOrAdd(type.FullName, _ => options.MemberAccessorStrategy.CreateConstructor(type));
}

private JsonDictionaryConverterState.DictionaryBuilderConstructorDelegate FindCachedDictionaryBuilderCtor(Type dictionaryType, JsonSerializerOptions options)
{
JsonPropertyInfo collectionPropertyInfo = state.Current.JsonPropertyInfo;
JsonPropertyInfo elementPropertyInfo = options.GetJsonPropertyInfoFromClassInfo(collectionPropertyInfo.ElementType, options);
return elementPropertyInfo.CreateDerivedDictionaryInstance(ref state, collectionPropertyInfo, sourceDictionary);
return s_dictionaryBuilderCtors.GetOrAdd(dictionaryType.FullName, _ => options.MemberAccessorStrategy.CreateDictionaryBuilderConstructor(dictionaryType));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,136 @@
// See the LICENSE file in the project root for more information.

using System.Collections;
using System.Collections.Generic;
using System.Collections.Concurrent;
using System.Collections.ObjectModel;
using System.Diagnostics;

namespace System.Text.Json.Serialization.Converters
{
internal sealed class DefaultDerivedEnumerableConverter : JsonEnumerableConverter
{
public override IEnumerable CreateFromList(ref ReadStack state, IList sourceList, JsonSerializerOptions options)
// Cache constructors for performance.
private static readonly ConcurrentDictionary<string, JsonClassInfo.ConstructorDelegate> s_ctors = new ConcurrentDictionary<string, JsonClassInfo.ConstructorDelegate>();
private static readonly ConcurrentDictionary<string, JsonEnumerableConverterState.CollectionBuilderConstructorDelegate> s_collectonBuilderCtors = new ConcurrentDictionary<string, JsonEnumerableConverterState.CollectionBuilderConstructorDelegate>();

public override bool OwnsImplementedCollectionType(Type declaredPropertyType, Type implementedCollectionType, Type collectionElementType)
{
return typeof(IEnumerable).IsAssignableFrom(implementedCollectionType);
}

public override Type ResolveRunTimeType(JsonPropertyInfo jsonPropertyInfo)
{
Type implementedCollectionPropertyType = jsonPropertyInfo.ImplementedCollectionPropertyType;

if (jsonPropertyInfo.DeclaredPropertyType.IsInterface)
{
if (implementedCollectionPropertyType.IsGenericType)
{
switch (implementedCollectionPropertyType.GetGenericTypeDefinition().FullName)
{
case JsonClassInfo.SetGenericInterfaceTypeName:
return typeof(HashSet<>).MakeGenericType(jsonPropertyInfo.CollectionElementType);
case JsonClassInfo.CollectionGenericInterfaceTypeName:
return typeof(Collection<>).MakeGenericType(jsonPropertyInfo.CollectionElementType);
}
}
return typeof(List<>).MakeGenericType(jsonPropertyInfo.CollectionElementType);
}

return jsonPropertyInfo.RuntimePropertyType;
}

public override void BeginEnumerable(ref ReadStack state, JsonSerializerOptions options)
{
Debug.Assert(state.Current.EnumerableConverterState == null);

object instance = CreateConcreteInstance(ref state, options);

if (instance is IList list)
{
state.Current.EnumerableConverterState = new JsonEnumerableConverterState
{
FinalList = list
};
}
else
{
JsonPropertyInfo jsonPropertyInfo = state.Current.JsonPropertyInfo;

Type collectionType = typeof(JsonEnumerableConverterState.CollectionBuilder<>).MakeGenericType(jsonPropertyInfo.CollectionElementType);

state.Current.EnumerableConverterState = new JsonEnumerableConverterState
{
Builder = CreateCollectionBuilderInstance(collectionType, jsonPropertyInfo, instance, options)
};
}
}

public override void AddItemToEnumerable<T>(ref Utf8JsonReader reader, ref ReadStack state, JsonSerializerOptions options, ref T value)
{
Debug.Assert(state.Current.EnumerableConverterState?.FinalList != null ||
state.Current.EnumerableConverterState.Builder != null);

IList finalList = state.Current.EnumerableConverterState.FinalList;

if (finalList != null)
{
if (finalList is IList<T> typedList)
{
typedList.Add(value);
}
else
{
finalList.Add(value);
}
}
else
{
state.Current.EnumerableConverterState.Builder.Add(ref value);
}
}

public override object EndEnumerable(ref ReadStack state, JsonSerializerOptions options)
{
Debug.Assert(state.Current.EnumerableConverterState?.FinalList != null ||
state.Current.EnumerableConverterState.Builder != null);

return state.Current.EnumerableConverterState.FinalList ?? state.Current.EnumerableConverterState.Builder.Instance;
}

private object CreateConcreteInstance(ref ReadStack state, JsonSerializerOptions options)
{
if (state.Current.JsonPropertyInfo.DeclaredPropertyType.IsInterface)
{
JsonClassInfo.ConstructorDelegate ctor = FindCachedCtor(state.Current.JsonPropertyInfo.RuntimePropertyType, options);
if (ctor == null)
{
ThrowHelper.ThrowInvalidOperationException_DeserializePolymorphicInterface(state.Current.JsonPropertyInfo.RuntimePropertyType);
}
return ctor();
}
else
{
return state.Current.JsonPropertyInfo.DeclaredClassInfo.CreateObject();
}
}

private JsonEnumerableConverterState.CollectionBuilder CreateCollectionBuilderInstance(Type collectionType, JsonPropertyInfo source, object instance, JsonSerializerOptions options)
{
JsonEnumerableConverterState.CollectionBuilderConstructorDelegate ctor = FindCachedCollectionBuilderCtor(collectionType, options);
Debug.Assert(ctor != null);
return ctor(source, instance);
}

private JsonClassInfo.ConstructorDelegate FindCachedCtor(Type type, JsonSerializerOptions options)
{
return s_ctors.GetOrAdd(type.FullName, _ => options.MemberAccessorStrategy.CreateConstructor(type));
}

private JsonEnumerableConverterState.CollectionBuilderConstructorDelegate FindCachedCollectionBuilderCtor(Type collectionType, JsonSerializerOptions options)
{
JsonPropertyInfo collectionPropertyInfo = state.Current.JsonPropertyInfo;
JsonPropertyInfo elementPropertyInfo = options.GetJsonPropertyInfoFromClassInfo(collectionPropertyInfo.ElementType, options);
return elementPropertyInfo.CreateDerivedEnumerableInstance(ref state, collectionPropertyInfo, sourceList);
return s_collectonBuilderCtors.GetOrAdd(collectionType.FullName, _ => options.MemberAccessorStrategy.CreateCollectionBuilderConstructor(collectionType));
}
}
}
Loading