Skip to content
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
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>
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,7 @@ internal enum ClassType
Value = 2,
// IEnumerable
Enumerable = 3,
// Is deserialized by passing a IList to its constructor
// i.e. immutable collections, readonly collections
ICollectionConstructible = 4,
// IDictionary
Dictionary = 5,
// Is deserialized by passing a IDictionary to its constructor
// i.e. immutable dictionaries, readonly dictionaries
IDictionaryConstructible = 6
Dictionary = 4,
}
}
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