Skip to content
This repository was archived by the owner on Jan 23, 2023. It is now read-only.
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
Original file line number Diff line number Diff line change
Expand Up @@ -28,16 +28,17 @@ private JsonPropertyInfo AddProperty(Type propertyType, PropertyInfo propertyInf
JsonPropertyInfo jsonInfo;

// Get implemented type, if applicable.
// Will return the propertyType itself if it's a non-enumerable, string, or natively supported collection.
Type implementedType = GetImplementedCollectionType(propertyType);
// Will return the propertyType itself if it's a non-enumerable, string, natively supported collection,
// or if a custom converter has been provided for the type.
Type implementedType = GetImplementedCollectionType(classType, propertyType, propertyInfo, out JsonConverter converter, options);

if (implementedType != propertyType)
{
jsonInfo = CreateProperty(implementedType, implementedType, implementedType, propertyInfo, typeof(object), options);
jsonInfo = CreateProperty(implementedType, implementedType, implementedType, propertyInfo, typeof(object), converter, options);
}
else
{
jsonInfo = CreateProperty(propertyType, propertyType, propertyType, propertyInfo, classType, options);
jsonInfo = CreateProperty(propertyType, propertyType, propertyType, propertyInfo, classType, converter, options);
}

// Convert non-immutable dictionary interfaces to concrete types.
Expand All @@ -48,11 +49,11 @@ private JsonPropertyInfo AddProperty(Type propertyType, PropertyInfo propertyInf
Type newPropertyType = elementPropertyInfo.GetDictionaryConcreteType();
if (implementedType != newPropertyType)
{
jsonInfo = CreateProperty(propertyType, newPropertyType, implementedType, propertyInfo, classType, options);
jsonInfo = CreateProperty(propertyType, newPropertyType, implementedType, propertyInfo, classType, converter, options);
}
else
{
jsonInfo = CreateProperty(propertyType, implementedType, implementedType, propertyInfo, classType, options);
jsonInfo = CreateProperty(propertyType, implementedType, implementedType, propertyInfo, classType, converter, options);
}
}
else if (jsonInfo.ClassType == ClassType.Enumerable &&
Expand All @@ -66,16 +67,16 @@ private JsonPropertyInfo AddProperty(Type propertyType, PropertyInfo propertyInf
Type newPropertyType = elementPropertyInfo.GetConcreteType(implementedType);
if ((implementedType != newPropertyType) && implementedType.IsAssignableFrom(newPropertyType))
{
jsonInfo = CreateProperty(propertyType, newPropertyType, implementedType, propertyInfo, classType, options);
jsonInfo = CreateProperty(propertyType, newPropertyType, implementedType, propertyInfo, classType, converter, options);
}
else
{
jsonInfo = CreateProperty(propertyType, implementedType, implementedType, propertyInfo, classType, options);
jsonInfo = CreateProperty(propertyType, implementedType, implementedType, propertyInfo, classType, converter, options);
}
}
else if (propertyType != implementedType)
{
jsonInfo = CreateProperty(propertyType, implementedType, implementedType, propertyInfo, classType, options);
jsonInfo = CreateProperty(propertyType, implementedType, implementedType, propertyInfo, classType, converter, options);
}

return jsonInfo;
Expand All @@ -87,6 +88,7 @@ internal static JsonPropertyInfo CreateProperty(
Type implementedPropertyType,
PropertyInfo propertyInfo,
Type parentClassType,
JsonConverter converter,
JsonSerializerOptions options)
{
bool hasIgnoreAttribute = (JsonPropertyInfo.GetAttribute<JsonIgnoreAttribute>(propertyInfo) != null);
Expand All @@ -106,15 +108,17 @@ internal static JsonPropertyInfo CreateProperty(
break;
}

JsonConverter converter;

// Create the JsonPropertyInfo<TType, TProperty>
Type propertyInfoClassType;
if (runtimePropertyType.IsGenericType && runtimePropertyType.GetGenericTypeDefinition() == typeof(Nullable<>))
{
// First try to find a converter for the Nullable, then if not found use the underlying type.
// This supports custom converters that want to (de)serialize as null when the value is not null.
converter = options.DetermineConverterForProperty(parentClassType, runtimePropertyType, propertyInfo);
if (converter == null)
{
converter = options.DetermineConverterForProperty(parentClassType, runtimePropertyType, propertyInfo);
}

if (converter != null)
{
propertyInfoClassType = typeof(JsonPropertyInfoNotNullable<,,,>).MakeGenericType(
Expand All @@ -132,7 +136,11 @@ internal static JsonPropertyInfo CreateProperty(
}
else
{
converter = options.DetermineConverterForProperty(parentClassType, runtimePropertyType, propertyInfo);
if (converter == null)
{
converter = options.DetermineConverterForProperty(parentClassType, runtimePropertyType, propertyInfo);
}

Type typeToConvert = converter?.TypeToConvert;
if (typeToConvert == null)
{
Expand Down Expand Up @@ -182,12 +190,25 @@ internal static JsonPropertyInfo CreateProperty(

internal JsonPropertyInfo CreateRootObject(JsonSerializerOptions options)
{
return CreateProperty(Type, Type, Type, null, Type, options);
return CreateProperty(
declaredPropertyType: Type,
runtimePropertyType: Type,
implementedPropertyType: Type,
propertyInfo: null,
parentClassType: Type,
converter: null,
options: options);
}

internal JsonPropertyInfo CreatePolymorphicProperty(JsonPropertyInfo property, Type runtimePropertyType, JsonSerializerOptions options)
{
JsonPropertyInfo runtimeProperty = CreateProperty(property.DeclaredPropertyType, runtimePropertyType, property.ImplementedPropertyType, property.PropertyInfo, Type, options);
JsonPropertyInfo runtimeProperty = CreateProperty(
property.DeclaredPropertyType, runtimePropertyType,
property.ImplementedPropertyType,
property.PropertyInfo,
parentClassType: Type,
converter: null,
options: options);
property.CopyRuntimeSettingsTo(runtimeProperty);

return runtimeProperty;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Text.Json.Serialization;
using System.Text.Json.Serialization.Converters;

namespace System.Text.Json
Expand Down Expand Up @@ -126,7 +127,12 @@ internal partial class JsonClassInfo
SortedListTypeName,
};

public static Type GetImplementedCollectionType(Type queryType)
public static Type GetImplementedCollectionType(
Type parentClassType,
Type queryType,
PropertyInfo propertyInfo,
out JsonConverter converter,
JsonSerializerOptions options)
{
Debug.Assert(queryType != null);

Expand All @@ -136,6 +142,14 @@ public static Type GetImplementedCollectionType(Type queryType)
queryType.IsInterface ||
queryType.IsArray ||
IsNativelySupportedCollection(queryType))
{
converter = null;
return queryType;
}

// If a converter was provided, we should not detect implemented types and instead use the converter later.
converter = options.DetermineConverterForProperty(parentClassType, queryType, propertyInfo);
if (converter != null)
{
return queryType;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -435,7 +435,7 @@ public static ulong GetKey(ReadOnlySpan<byte> propertyName)
public static Type GetElementType(Type propertyType, Type parentType, MemberInfo memberInfo, JsonSerializerOptions options)
{
// We want to handle as the implemented collection type, if applicable.
Type implementedType = GetImplementedCollectionType(propertyType);
Type implementedType = GetImplementedCollectionType(parentType, propertyType, null, out _, options);

if (!typeof(IEnumerable).IsAssignableFrom(implementedType))
{
Expand Down Expand Up @@ -484,7 +484,7 @@ public static ClassType GetClassType(Type type, JsonSerializerOptions options)
Debug.Assert(type != null);

// We want to handle as the implemented collection type, if applicable.
Type implementedType = GetImplementedCollectionType(type);
Type implementedType = GetImplementedCollectionType(typeof(object), type, null, out _, options);

if (implementedType.IsGenericType && implementedType.GetGenericTypeDefinition() == typeof(Nullable<>))
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -355,7 +355,7 @@ internal JsonPropertyInfo GetJsonPropertyInfoFromClassInfo(Type objectType, Json
{
if (!_objectJsonProperties.TryGetValue(objectType, out JsonPropertyInfo propertyInfo))
{
propertyInfo = JsonClassInfo.CreateProperty(objectType, objectType, objectType, null, typeof(object), options);
propertyInfo = JsonClassInfo.CreateProperty(objectType, objectType, objectType, null, typeof(object), null, options);
_objectJsonProperties[objectType] = propertyInfo;
}

Expand Down
Loading