Skip to content
Closed
Show file tree
Hide file tree
Changes from 1 commit
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
Prev Previous commit
Next Next commit
introduce dictionary overrideProperties
  • Loading branch information
devsko committed Apr 28, 2021
commit ad23d170f20691e89c8c5d4eff3514f4c14556c2
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,7 @@ internal JsonTypeInfo(Type type, JsonConverter converter, Type runtimeType, Json
: StringComparer.Ordinal);

Dictionary<string, MemberInfo>? ignoredMembers = null;
Dictionary<string, PropertyInfo>? overrideProperties = null;

// We start from the most derived type.
for (Type? currentType = type; currentType != null; currentType = currentType.BaseType)
Expand All @@ -216,7 +217,7 @@ internal JsonTypeInfo(Type type, JsonConverter converter, Type runtimeType, Json
if (propertyInfo.GetMethod?.IsPublic == true ||
propertyInfo.SetMethod?.IsPublic == true)
{
CacheMember(currentType, propertyInfo.PropertyType, propertyInfo, typeNumberHandling, cache, ref ignoredMembers);
CacheMember(currentType, propertyInfo.PropertyType, propertyInfo, typeNumberHandling, cache, ref ignoredMembers, ref overrideProperties);
}
else
{
Expand All @@ -237,7 +238,7 @@ internal JsonTypeInfo(Type type, JsonConverter converter, Type runtimeType, Json
{
if (hasJsonInclude || Options.IncludeFields)
{
CacheMember(currentType, fieldInfo.FieldType, fieldInfo, typeNumberHandling, cache, ref ignoredMembers);
CacheMember(currentType, fieldInfo.FieldType, fieldInfo, typeNumberHandling, cache, ref ignoredMembers, ref overrideProperties);
}
}
else
Expand Down Expand Up @@ -319,12 +320,14 @@ private void CacheMember(
MemberInfo memberInfo,
JsonNumberHandling? typeNumberHandling,
Dictionary<string, JsonPropertyInfo> cache,
ref Dictionary<string, MemberInfo>? ignoredMembers)
ref Dictionary<string, MemberInfo>? ignoredMembers,
ref Dictionary<string, PropertyInfo>? overrideProperties)
{
JsonPropertyInfo jsonPropertyInfo = AddProperty(memberInfo, memberType, declaringType, typeNumberHandling, Options);
Debug.Assert(jsonPropertyInfo.NameAsString != null);

string memberName = memberInfo.Name;
PropertyInfo? property = memberInfo as PropertyInfo;

// The JsonPropertyNameAttribute or naming policy resulted in a collision.
if (!JsonHelpers.TryAdd(cache, jsonPropertyInfo.NameAsString, jsonPropertyInfo, out JsonPropertyInfo? other))
Expand All @@ -340,7 +343,7 @@ private void CacheMember(
// Is the current property hidden by the previously cached property
// (with `new` keyword, or by overriding)?
other.MemberInfo!.Name != memberName &&
// Was a property with the same CLR name was ignored? That property hid the current property,
// Was a property with the same CLR name ignored? That property hid the current property,
// thus, if it was ignored, the current property should be ignored too.
ignoredMembers?.ContainsKey(memberName) != true)
{
Expand All @@ -349,11 +352,48 @@ private void CacheMember(
}
// Ignore the current property.
}
else if (overrideProperties != null &&
overrideProperties.TryGetValue(memberName, out PropertyInfo? overrideProperty) &&
property != null &&
IsOverrideOf(property, overrideProperty))
{
// This property is overridden in a derived class. Implicitly ignore it
// by removing from the cache, regardless of an applied JsonPropertyName attribute.
cache.Remove(jsonPropertyInfo.NameAsString);
}

if (jsonPropertyInfo.IsIgnored)
{
(ignoredMembers ??= new Dictionary<string, MemberInfo>()).Add(memberName, memberInfo);
}
else if (property != null && IsOverride(property))
{
(overrideProperties ??= new Dictionary<string, PropertyInfo>())[memberName] = property;
}
}

private static bool IsOverrideOf(PropertyInfo property, PropertyInfo other)
{
MethodInfo? getMethod = property.GetMethod;
if (getMethod != null)
{
return getMethod.GetBaseDefinition() == other.GetMethod?.GetBaseDefinition();
}

MethodInfo? setMethod = property.SetMethod;
return setMethod != null && setMethod.GetBaseDefinition() == other.SetMethod?.GetBaseDefinition();
}

private static bool IsOverride(PropertyInfo property)
{
MethodInfo? getMethod = property.GetMethod;
if (getMethod != null)
{
return getMethod.GetBaseDefinition() != getMethod;
}

MethodInfo? setMethod = property.SetMethod;
return setMethod != null && setMethod.GetBaseDefinition() == setMethod;
}

private sealed class ParameterLookupKey
Expand Down Expand Up @@ -473,8 +513,7 @@ private static bool PropertyIsOverridenAndIgnored(PropertyInfo currentProperty,
return false;
}

return currentProperty.GetMethod?.GetBaseDefinition() == ignoredProperty.GetMethod?.GetBaseDefinition() ||
currentProperty.SetMethod?.GetBaseDefinition() == ignoredProperty.SetMethod?.GetBaseDefinition();
return IsOverrideOf(currentProperty, ignoredProperty);
}

private bool DetermineExtensionDataProperty(Dictionary<string, JsonPropertyInfo> cache)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -637,7 +637,11 @@ public static void OverriddenPropertiesIgnored_WhenRenamed()
Assert.Equal(@"{""Renamed"":true}", serialized);

serialized = JsonSerializer.Serialize(new FurtherDerivedClass_WithRenamedOverride());
Assert.Equal(@"{""MyProp"":true}", serialized);
Assert.Equal(@"{""MyProp"":false}", serialized);

// Don't ignore virtual properties with same name and type when they are in a new slot.
serialized = JsonSerializer.Serialize(new FurtherDerivedClass_WithRenamedNewSlotVirtual());
Assert.Equal(@"{""MyProp"":false,""Renamed"":true}", serialized);
}

public class ClassWithInternalField
Expand Down Expand Up @@ -983,6 +987,11 @@ private class FurtherDerivedClass_WithRenamedOverride : DerivedClass_WithRenamed
public override bool MyProp { get; set; }
}

private class FurtherDerivedClass_WithRenamedNewSlotVirtual : DerivedClass_WithRenamedOverride
{
public new virtual bool MyProp { get; set; }
}

[Fact]
public static void IgnoreReadOnlyProperties()
{
Expand Down