string delegateKey = DefaultImmutableEnumerableConverter.GetDelegateKey(immutableCollectionType, elementType, out _, out _);
- JsonPropertyInfo propertyInfo = options.GetJsonPropertyInfoFromClassInfo(elementType, options);
- JsonPropertyInfo propertyInfo = elementClassInfo.PolicyProperty ?? elementClassInfo.CreateRootObject(options);
++ JsonPropertyInfo propertyInfo = elementClassInfo.PolicyProperty ?? elementClassInfo.CreateRootProperty(options);
+ Debug.Assert(propertyInfo != null);
return propertyInfo.CreateImmutableDictionaryInstance(ref state, immutableCollectionType, delegateKey, sourceDictionary, options);
}
}
string delegateKey = GetDelegateKey(immutableCollectionType, elementType, out _, out _);
- JsonPropertyInfo propertyInfo = options.GetJsonPropertyInfoFromClassInfo(elementType, options);
- JsonPropertyInfo propertyInfo = elementClassInfo.PolicyProperty ?? elementClassInfo.CreateRootObject(options);
++ JsonPropertyInfo propertyInfo = elementClassInfo.PolicyProperty ?? elementClassInfo.CreateRootProperty(options);
+ Debug.Assert(propertyInfo != null);
return propertyInfo.CreateImmutableCollectionInstance(ref state, immutableCollectionType, delegateKey, sourceList, options);
}
}
return jsonPropertyInfo;
}
- internal JsonPropertyInfo CreateRootObject(JsonSerializerOptions options)
+ /// <summary>
+ /// Create a <see cref="JsonPropertyInfo"/> for a given Type.
++ /// A policy property is not a real property on a type; instead it leverages the existing converter
++ /// logic and generic support to avoid boxing. It is used with values types, elements from collections and
++ /// dictionaries, and collections themselves. Typically it would represent a CLR type such as System.String.
++ /// </summary>
++ internal static JsonPropertyInfo CreatePolicyProperty(
++ Type declaredPropertyType,
++ Type runtimePropertyType,
++ Type elementType,
++ JsonConverter converter,
++ ClassType classType,
++ JsonSerializerOptions options)
++ {
++ return CreateProperty(
++ declaredPropertyType: declaredPropertyType,
++ runtimePropertyType: runtimePropertyType,
++ propertyInfo: null, // Not a real property so this is null.
++ parentClassType: typeof(object), // a dummy value (not used)
++ collectionElementType : elementType,
++ Nullable.GetUnderlyingType(runtimePropertyType),
++ converter : converter,
++ classType : classType,
++ options);
++ }
++
++ /// <summary>
++ /// Create a <see cref="JsonPropertyInfo"/> for a given Type.
+ /// </summary>
- internal static JsonPropertyInfo CreateRootProperty(Type type, JsonSerializerOptions options)
++ internal JsonPropertyInfo CreateRootProperty(JsonSerializerOptions options)
{
+ JsonConverter converter = options.DetermineConverterForProperty(Type, Type, propertyInfo: null);
+
return CreateProperty(
- declaredPropertyType: type,
- runtimePropertyType: type,
- implementedPropertyType: type,
+ declaredPropertyType: Type,
+ runtimePropertyType: Type,
propertyInfo: null,
- parentClassType: Type,
+ parentClassType: typeof(object), // a dummy value (not used)
- converter: null,
- options: options);
+ ElementType,
+ Nullable.GetUnderlyingType(Type),
+ converter,
+ ClassType,
+ options);
}
- internal JsonPropertyInfo CreatePolymorphicProperty(JsonPropertyInfo property, Type runtimePropertyType, JsonSerializerOptions options)
+ internal JsonPropertyInfo GetOrAddPolymorphicProperty(JsonPropertyInfo property, Type runtimePropertyType, JsonSerializerOptions options)
{
- JsonPropertyInfo runtimeProperty = CreateProperty(
- property.DeclaredPropertyType, runtimePropertyType,
- property.ImplementedPropertyType,
- property.PropertyInfo,
- parentClassType: Type,
- converter: null,
- options: options);
- property.CopyRuntimeSettingsTo(runtimeProperty);
-
- return runtimeProperty;
+ static JsonPropertyInfo CreateRuntimeProperty((JsonPropertyInfo property, Type runtimePropertyType) key, (JsonSerializerOptions options, Type classType) arg)
+ {
+ ClassType classType = GetClassType(
+ key.runtimePropertyType,
+ arg.classType,
+ key.property.PropertyInfo,
+ out _,
+ out Type elementType,
+ out Type nullableType,
+ out _,
+ out JsonConverter converter,
+ checkForAddMethod: false,
+ arg.options);
+
+ JsonPropertyInfo runtimeProperty = CreateProperty(
+ key.property.DeclaredPropertyType,
+ key.runtimePropertyType,
+ key.property.PropertyInfo,
+ parentClassType: arg.classType,
+ collectionElementType: elementType,
+ nullableType,
+ converter,
+ classType,
+ options: arg.options);
+ key.property.CopyRuntimeSettingsTo(runtimeProperty);
+
+ return runtimeProperty;
+ }
+
+ ConcurrentDictionary<(JsonPropertyInfo, Type), JsonPropertyInfo> cache =
+ LazyInitializer.EnsureInitialized(ref RuntimePropertyCache, () => new ConcurrentDictionary<(JsonPropertyInfo, Type), JsonPropertyInfo>());
+ #if BUILDING_INBOX_LIBRARY
+ return cache.GetOrAdd((property, runtimePropertyType), (key, arg) => CreateRuntimeProperty(key, arg), (options, Type));
+ #else
+ return cache.GetOrAdd((property, runtimePropertyType), key => CreateRuntimeProperty(key, (options, Type)));
+ #endif
}
}
}
case ClassType.Enumerable:
case ClassType.Dictionary:
{
-- // Add a single property that maps to the class type so we can have policies applied.
- AddPolicyProperty(type, options);
-
- Type objectType;
- if (IsNativelySupportedCollection(type))
- {
- // Use the type from the property policy to get any late-bound concrete types (from an interface like IDictionary).
- objectType = PolicyProperty.RuntimePropertyType;
- }
- else
- {
- // We need to create the declared instance for types implementing natively supported collections.
- objectType = PolicyProperty.DeclaredPropertyType;
- }
-
- CreateObject = options.MemberAccessorStrategy.CreateConstructor(objectType);
-
- ElementType = GetElementType(type, parentType: null, memberInfo: null, options: options);
+ ElementType = elementType;
+ AddItemToObject = addMethod;
-
- // A policy property is not a real property on a type; instead it leverages the existing converter
- // logic and generic support to avoid boxing. It is used with values types, elements from collections and
- // dictionaries, and collections themselves. Typically it would represent a CLR type such as System.String.
- PolicyProperty = CreateProperty(
- declaredPropertyType: type,
- runtimePropertyType: runtimeType,
- propertyInfo: null, // Not a real property so this is null.
- parentClassType: typeof(object),
- collectionElementType: elementType,
- nullableUnderlyingType,
- converter: null,
- ClassType,
- options);
-
++ PolicyProperty = CreatePolicyProperty(type, runtimeType, elementType, converter: null, ClassType, options);
+ CreateObject = options.MemberAccessorStrategy.CreateConstructor(PolicyProperty.RuntimePropertyType);
}
break;
- case ClassType.IDictionaryConstructible:
+ case ClassType.Value:
{
- // Add a single property that maps to the class type so we can have policies applied.
- AddPolicyProperty(type, options);
-
- ElementType = GetElementType(type, parentType: null, memberInfo: null, options: options);
-
- CreateConcreteDictionary = options.MemberAccessorStrategy.CreateConstructor(
- typeof(Dictionary<,>).MakeGenericType(typeof(string), ElementType));
-
- CreateObject = options.MemberAccessorStrategy.CreateConstructor(PolicyProperty.DeclaredPropertyType);
+ CreateObject = options.MemberAccessorStrategy.CreateConstructor(type);
-
- // Add a single property that maps to the class type so we can have policies applied.
- //AddPolicyPropertyForValue(type, options);
- PolicyProperty = CreateProperty(
- declaredPropertyType: type,
- runtimePropertyType: runtimeType,
- propertyInfo: null, // Not a real property so this is null.
- parentClassType: typeof(object),
- collectionElementType: null,
- nullableUnderlyingType,
- converter,
- ClassType,
- options);
++ PolicyProperty = CreatePolicyProperty(type, runtimeType, elementType: null, converter, ClassType, options);
}
break;
- case ClassType.Value:
- // Add a single property that maps to the class type so we can have policies applied.
- AddPolicyProperty(type, options);
- break;
case ClassType.Unknown:
- // Add a single property that maps to the class type so we can have policies applied.
- AddPolicyProperty(type, options);
- PropertyCache = new Dictionary<string, JsonPropertyInfo>();
- PropertyCacheArray = Array.Empty<JsonPropertyInfo>();
+ {
+ CreateObject = options.MemberAccessorStrategy.CreateConstructor(type);
-
- // Add a single property that maps to the class type so we can have policies applied.
- //AddPolicyPropertyForValue(type, options);
- PolicyProperty = CreateProperty(
- declaredPropertyType: type,
- runtimePropertyType: runtimeType,
- propertyInfo: null, // Not a real property so this is null.
- parentClassType: typeof(object),
- collectionElementType: null,
- nullableUnderlyingType,
- converter,
- ClassType,
- options);
-
++ PolicyProperty = CreatePolicyProperty(type, runtimeType, elementType: null, converter, ClassType, options);
+ PropertyCache = new Dictionary<string, JsonPropertyInfo>();
+ PropertyCacheArray = Array.Empty<JsonPropertyInfo>();
+ }
break;
default:
Debug.Fail($"Unexpected class type: {ClassType}");
private JsonClassInfo _runtimeClassInfo;
private JsonClassInfo _declaredTypeClassInfo;
+ private JsonPropertyInfo _dictionaryValuePropertyPolicy;
+
public bool CanBeNull { get; private set; }
+ public bool IsImmutableArray { get; private set; }
public ClassType ClassType;
}
/// <summary>
- Debug.Assert(ClassType == ClassType.Dictionary || ClassType == ClassType.IDictionaryConstructible);
-
- Type dictionaryValueType = ElementType;
- Debug.Assert(ElementType != null);
-
- _dictionaryValuePropertyPolicy = JsonClassInfo.CreateRootProperty(dictionaryValueType, Options);
+ /// Return the JsonPropertyInfo for the TValue in IDictionary{string, TValue} when deserializing.
++ /// This only needs to contain the raw TValue and does not need converter, etc applied since it
++ /// is only used for "casting" reasons.
+ /// </summary>
+ /// <remarks>
+ /// This should not be called during warm-up (initial creation of JsonPropertyInfos) to avoid recursive behavior
+ /// which could result in a StackOverflowException.
+ /// </remarks>
+ public JsonPropertyInfo DictionaryValuePropertyPolicy
+ {
+ get
+ {
++ Debug.Assert(ClassType == ClassType.Dictionary);
++
+ if (_dictionaryValuePropertyPolicy == null)
+ {
++ //if ((_dictionaryValuePropertyPolicy = ElementClassInfo.PolicyProperty) == null)
++ {
++ Type dictionaryValueType = ElementType;
++ Debug.Assert(dictionaryValueType != null);
++
++ _dictionaryValuePropertyPolicy = JsonClassInfo.CreatePolicyProperty(
++ declaredPropertyType : dictionaryValueType,
++ runtimePropertyType : dictionaryValueType,
++ elementType : null,
++ converter: null,
++ ClassType.Dictionary,
++ Options);
++ }
+ }
+
+ return _dictionaryValuePropertyPolicy;
+ }
+ }
+
+ /// <summary>
/// Return the JsonClassInfo for the element type, or null if the property is not an enumerable or dictionary.
/// </summary>
/// <remarks>
{
if (_elementClassInfo == null && ElementType != null)
{
-- Debug.Assert(ClassType == ClassType.Enumerable ||
- ClassType == ClassType.Dictionary ||
- ClassType == ClassType.IDictionaryConstructible);
- ClassType == ClassType.Dictionary);
++ Debug.Assert(ClassType == ClassType.Enumerable || ClassType == ClassType.Dictionary);
_elementClassInfo = Options.GetOrAddClass(ElementType);
}
return (TAttribute)propertyInfo?.GetCustomAttribute(typeof(TAttribute), inherit: false);
}
- public abstract Type GetConcreteType(Type type);
-
public abstract Type GetDictionaryConcreteType();
- Debug.Assert(ClassType == ClassType.Dictionary ||
- ClassType == ClassType.IDictionaryConstructible);
+ public void GetDictionaryKeyAndValue(ref WriteStackFrame writeStackFrame, out string key, out object value)
+ {
++ Debug.Assert(ClassType == ClassType.Dictionary);
+
+ if (writeStackFrame.CollectionEnumerator is IDictionaryEnumerator iDictionaryEnumerator)
+ {
+ // Since IDictionaryEnumerator is not based on generics we can obtain the value directly.
+ key = (string)iDictionaryEnumerator.Key;
+ value = iDictionaryEnumerator.Value;
+ }
+ else
+ {
+ // Forward to the generic dictionary.
+ DictionaryValuePropertyPolicy.GetDictionaryKeyAndValueFromGenericDictionary(ref writeStackFrame, out key, out value);
+ }
+ }
+
+ public abstract void GetDictionaryKeyAndValueFromGenericDictionary(ref WriteStackFrame writeStackFrame, out string key, out object value);
+
public virtual void GetPolicies()
{
DetermineSerializationCapabilities();
}
}
- public override IList CreateConverterList()
- {
- return new List<TDeclaredProperty>();
- }
+ private JsonPropertyInfo _elementPropertyInfo;
- public override Type GetConcreteType(Type parentType)
+ private void SetPropertyInfoForObjectElement()
{
- if (JsonClassInfo.IsDeserializedByAssigningFromList(parentType))
- {
- return typeof(List<TDeclaredProperty>);
- }
- else if (JsonClassInfo.IsSetInterface(parentType))
+ if (_elementPropertyInfo == null && ElementClassInfo.PolicyProperty == null)
{
- return typeof(HashSet<TDeclaredProperty>);
- _elementPropertyInfo = ElementClassInfo.CreateRootObject(Options);
++ _elementPropertyInfo = ElementClassInfo.CreateRootProperty(Options);
}
-
- return parentType;
}
- public override IEnumerable CreateDerivedEnumerableInstance(ref ReadStack state, JsonPropertyInfo collectionPropertyInfo, IList sourceList)
+ public override bool TryCreateEnumerableAddMethod(object target, out object addMethodDelegate)
{
- // Implementing types that don't have default constructors are not supported for deserialization.
- if (collectionPropertyInfo.DeclaredTypeClassInfo.CreateObject == null)
- {
- throw ThrowHelper.GetNotSupportedException_SerializationNotSupportedCollection(
- collectionPropertyInfo.DeclaredPropertyType,
- collectionPropertyInfo.ParentClassType,
- collectionPropertyInfo.PropertyInfo);
- }
+ SetPropertyInfoForObjectElement();
+ Debug.Assert((_elementPropertyInfo ?? ElementClassInfo.PolicyProperty) != null);
- object instance = collectionPropertyInfo.DeclaredTypeClassInfo.CreateObject();
+ addMethodDelegate = (_elementPropertyInfo ?? ElementClassInfo.PolicyProperty).CreateEnumerableAddMethod(RuntimeClassInfo.AddItemToObject, target);
+ return addMethodDelegate != null;
+ }
- if (instance is IList instanceOfIList)
- {
- if (!instanceOfIList.IsReadOnly)
- {
- foreach (object item in sourceList)
- {
- instanceOfIList.Add(item);
- }
- return instanceOfIList;
- }
- }
- else if (instance is ICollection<TDeclaredProperty> instanceOfICollection)
- {
- if (!instanceOfICollection.IsReadOnly)
- {
- foreach (TDeclaredProperty item in sourceList)
- {
- instanceOfICollection.Add(item);
- }
- return instanceOfICollection;
- }
- }
- else if (instance is Stack<TDeclaredProperty> instanceOfStack)
- {
- foreach (TDeclaredProperty item in sourceList)
- {
- instanceOfStack.Push(item);
- }
- return instanceOfStack;
- }
- else if (instance is Queue<TDeclaredProperty> instanceOfQueue)
+ public override object CreateEnumerableAddMethod(MethodInfo addMethod, object target)
+ {
+ if (target is ICollection<TDeclaredProperty> collection && collection.IsReadOnly)
{
- foreach (TDeclaredProperty item in sourceList)
- {
- instanceOfQueue.Enqueue(item);
- }
- return instanceOfQueue;
+ return null;
}
- // TODO (https://github.com/dotnet/corefx/issues/40479):
- // Use reflection to support types implementing Stack or Queue.
+ return Options.MemberAccessorStrategy.CreateAddDelegate<TDeclaredProperty>(addMethod, target);
+ }
- throw ThrowHelper.GetNotSupportedException_SerializationNotSupportedCollection(
- collectionPropertyInfo.DeclaredPropertyType,
- collectionPropertyInfo.ParentClassType,
- collectionPropertyInfo.PropertyInfo);
+ public override void AddObjectToEnumerableWithReflection(object addMethodDelegate, object value)
+ {
+ Debug.Assert((_elementPropertyInfo ?? ElementClassInfo.PolicyProperty) != null);
+ (_elementPropertyInfo ?? ElementClassInfo.PolicyProperty).AddObjectToParentEnumerable(addMethodDelegate, value);
}
- public override object CreateDerivedDictionaryInstance(ref ReadStack state, JsonPropertyInfo collectionPropertyInfo, IDictionary sourceDictionary)
+ public override void AddObjectToParentEnumerable(object addMethodDelegate, object value)
{
- // Implementing types that don't have default constructors are not supported for deserialization.
- if (collectionPropertyInfo.DeclaredTypeClassInfo.CreateObject == null)
- {
- throw ThrowHelper.GetNotSupportedException_SerializationNotSupportedCollection(
- collectionPropertyInfo.DeclaredPropertyType,
- collectionPropertyInfo.ParentClassType,
- collectionPropertyInfo.PropertyInfo);
- }
+ ((Action<TDeclaredProperty>)addMethodDelegate)((TDeclaredProperty)value);
+ }
- object instance = collectionPropertyInfo.DeclaredTypeClassInfo.CreateObject();
+ public override void AddObjectToDictionary(object target, string key, object value)
+ {
+ Debug.Assert((_elementPropertyInfo ?? ElementClassInfo.PolicyProperty) != null);
+ (_elementPropertyInfo ?? ElementClassInfo.PolicyProperty).AddObjectToParentDictionary(target, key, value);
+ }
- if (instance is IDictionary instanceOfIDictionary)
+ public override void AddObjectToParentDictionary(object target, string key, object value)
+ {
+ if (target is IDictionary<string, TDeclaredProperty> genericDict)
{
- if (!instanceOfIDictionary.IsReadOnly)
- {
- foreach (DictionaryEntry entry in sourceDictionary)
- {
- instanceOfIDictionary.Add((string)entry.Key, entry.Value);
- }
- return instanceOfIDictionary;
- }
+ Debug.Assert(!genericDict.IsReadOnly);
+ genericDict[key] = (TDeclaredProperty)value;
}
- else if (instance is IDictionary<string, TDeclaredProperty> instanceOfGenericIDictionary)
+ else
{
- if (!instanceOfGenericIDictionary.IsReadOnly)
- {
- foreach (DictionaryEntry entry in sourceDictionary)
- {
- instanceOfGenericIDictionary.Add((string)entry.Key, (TDeclaredProperty)entry.Value);
- }
- return instanceOfGenericIDictionary;
- }
+ throw ThrowHelper.GetNotSupportedException_SerializationNotSupportedCollection(target.GetType(), parentType: null, memberInfo: null);
}
+ }
- // TODO (https://github.com/dotnet/corefx/issues/40479):
- // Use reflection to support types implementing SortedList and maybe immutable dictionaries.
-
- // Types implementing SortedList and immutable dictionaries will fail here.
- throw ThrowHelper.GetNotSupportedException_SerializationNotSupportedCollection(
- collectionPropertyInfo.DeclaredPropertyType,
- collectionPropertyInfo.ParentClassType,
- collectionPropertyInfo.PropertyInfo);
+ public override bool CanPopulateDictionary(object target)
+ {
+ SetPropertyInfoForObjectElement();
+ Debug.Assert((_elementPropertyInfo ?? ElementClassInfo.PolicyProperty) != null);
+ return (_elementPropertyInfo ?? ElementClassInfo.PolicyProperty).ParentDictionaryCanBePopulated(target);
}
- public override IEnumerable CreateIEnumerableInstance(ref ReadStack state, Type parentType, IList sourceList)
+ public override bool ParentDictionaryCanBePopulated(object target)
{
- if (parentType.IsGenericType)
+ if (target is IDictionary<string, TDeclaredProperty> genericDict && !genericDict.IsReadOnly)
{
- Type genericTypeDefinition = parentType.GetGenericTypeDefinition();
- IEnumerable<TDeclaredProperty> items = CreateGenericTDeclaredPropertyIEnumerable(sourceList);
-
- if (genericTypeDefinition == typeof(Stack<>))
- {
- return new Stack<TDeclaredProperty>(items);
- }
- else if (genericTypeDefinition == typeof(Queue<>))
- {
- return new Queue<TDeclaredProperty>(items);
- }
- else if (genericTypeDefinition == typeof(HashSet<>))
- {
- return new HashSet<TDeclaredProperty>(items);
- }
- else if (genericTypeDefinition == typeof(LinkedList<>))
- {
- return new LinkedList<TDeclaredProperty>(items);
- }
- else if (genericTypeDefinition == typeof(SortedSet<>))
- {
- return new SortedSet<TDeclaredProperty>(items);
- }
-
- return (IEnumerable)Activator.CreateInstance(parentType, items);
+ return true;
}
- else
+ else if (target is IDictionary dict && !dict.IsReadOnly)
{
- if (parentType == typeof(ArrayList))
- {
- return new ArrayList(sourceList);
- }
- // Stack and Queue go into this condition, until we support with reflection.
- else
+ Type genericDictType = target.GetType().GetInterface("System.Collections.Generic.IDictionary`2") ??
+ target.GetType().GetInterface("System.Collections.Generic.IReadOnlyDictionary`2");
+
+ if (genericDictType != null && genericDictType.GetGenericArguments()[0] != typeof(string))
{
- return (IEnumerable)Activator.CreateInstance(parentType, sourceList);
+ return false;
}
+
+ return true;
}
+
+ return false;
}
- public override IDictionary CreateIDictionaryInstance(ref ReadStack state, Type parentType, IDictionary sourceDictionary)
+ public override IList CreateConverterList()
{
- if (parentType.FullName == JsonClassInfo.HashtableTypeName)
- {
- return new Hashtable(sourceDictionary);
- }
- // SortedList goes into this condition, unless we add a ref to System.Collections.NonGeneric.
- else
- {
- return (IDictionary)Activator.CreateInstance(parentType, sourceDictionary);
- }
+ return new List<TDeclaredProperty>();
+ }
+
+ public override IDictionary CreateConverterDictionary()
+ {
+ return new Dictionary<string, TDeclaredProperty>();
}
// Creates an IEnumerable<TDeclaredPropertyType> and populates it with the items in the
JsonPropertyInfo jsonPropertyInfo = state.Current.JsonPropertyInfo;
if (jsonPropertyInfo == null)
{
- jsonPropertyInfo = JsonClassInfo.CreateRootProperty(state.Current.JsonClassInfo.Type, options);
- jsonPropertyInfo = state.Current.JsonClassInfo.CreateRootObject(options);
++ jsonPropertyInfo = state.Current.JsonClassInfo.CreateRootProperty(options);
}
else if (state.Current.JsonClassInfo.ClassType == ClassType.Unknown)
{
JsonPropertyInfo jsonPropertyInfo = state.Current.JsonPropertyInfo;
if (jsonPropertyInfo == null)
{
- jsonPropertyInfo = JsonClassInfo.CreateRootProperty(state.Current.JsonClassInfo.Type, options);
- jsonPropertyInfo = state.Current.JsonClassInfo.CreateRootObject(options);
++ jsonPropertyInfo = state.Current.JsonClassInfo.CreateRootProperty(options);
}
Debug.Assert(jsonPropertyInfo != null);
JsonPropertyInfo jsonPropertyInfo = state.Current.JsonPropertyInfo;
if (jsonPropertyInfo == null)
{
- jsonPropertyInfo = JsonClassInfo.CreateRootProperty(state.Current.JsonClassInfo.Type, options);
- jsonPropertyInfo = state.Current.JsonClassInfo.CreateRootObject(options);
++ jsonPropertyInfo = state.Current.JsonClassInfo.CreateRootProperty(options);
}
else if (state.Current.JsonClassInfo.ClassType == ClassType.Unknown)
{