Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixed serialization of hidden base class members #32107

Merged
merged 16 commits into from
May 8, 2020
Merged
Original file line number Diff line number Diff line change
Expand Up @@ -97,52 +97,58 @@ public JsonClassInfo(Type type, JsonSerializerOptions options)
case ClassType.Object:
{
CreateObject = options.MemberAccessorStrategy.CreateConstructor(type);
Dictionary<string, JsonPropertyInfo> cache = new Dictionary<string, JsonPropertyInfo>(
Options.PropertyNameCaseInsensitive
? StringComparer.OrdinalIgnoreCase
: StringComparer.Ordinal);

PropertyInfo[] properties = type.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);

Dictionary<string, JsonPropertyInfo> cache = CreatePropertyCache(properties.Length);

foreach (PropertyInfo propertyInfo in properties)
for (Type? currentType = type; currentType != null; currentType = currentType.BaseType)
{
// Ignore indexers
if (propertyInfo.GetIndexParameters().Length > 0)
foreach (PropertyInfo propertyInfo in currentType.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.DeclaredOnly))
{
continue;
}

if (IsNonPublicProperty(propertyInfo))
{
if (JsonPropertyInfo.GetAttribute<JsonIncludeAttribute>(propertyInfo) != null)
// Ignore indexers
if (propertyInfo.GetIndexParameters().Length > 0)
{
ThrowHelper.ThrowInvalidOperationException_JsonIncludeOnNonPublicInvalid(propertyInfo, Type);
continue;
}

// Non-public properties should not be included for (de)serialization.
continue;
}
if (IsNonPublicProperty(propertyInfo))
{
if (JsonPropertyInfo.GetAttribute<JsonIncludeAttribute>(propertyInfo) != null)
{
ThrowHelper.ThrowInvalidOperationException_JsonIncludeOnNonPublicInvalid(propertyInfo, currentType);
}

// For now we only support public getters\setters
if (propertyInfo.GetMethod?.IsPublic == true ||
propertyInfo.SetMethod?.IsPublic == true)
{
JsonPropertyInfo jsonPropertyInfo = AddProperty(propertyInfo, type, options);
Debug.Assert(jsonPropertyInfo != null && jsonPropertyInfo.NameAsString != null);
// Non-public properties should not be included for (de)serialization.
continue;
}

// If the JsonPropertyNameAttribute or naming policy results in collisions, throw an exception.
if (!JsonHelpers.TryAdd(cache, jsonPropertyInfo.NameAsString, jsonPropertyInfo))
// For now we only support public getters\setters
if (propertyInfo.GetMethod?.IsPublic == true ||
propertyInfo.SetMethod?.IsPublic == true)
{
JsonPropertyInfo other = cache[jsonPropertyInfo.NameAsString];
JsonPropertyInfo jsonPropertyInfo = AddProperty(propertyInfo, currentType, options);
Debug.Assert(jsonPropertyInfo != null && jsonPropertyInfo.NameAsString != null);

if (other.ShouldDeserialize == false && other.ShouldSerialize == false)
{
// Overwrite the one just added since it has [JsonIgnore].
cache[jsonPropertyInfo.NameAsString] = jsonPropertyInfo;
}
else if (jsonPropertyInfo.ShouldDeserialize == true || jsonPropertyInfo.ShouldSerialize == true)
// If the JsonPropertyNameAttribute or naming policy results in collisions, throw an exception.
if (!JsonHelpers.TryAdd(cache, jsonPropertyInfo.NameAsString, jsonPropertyInfo))
{
ThrowHelper.ThrowInvalidOperationException_SerializerPropertyNameConflict(Type, jsonPropertyInfo);
JsonPropertyInfo other = cache[jsonPropertyInfo.NameAsString];

if (other.ShouldDeserialize == false && other.ShouldSerialize == false)
{
// Overwrite the one just added since it has [JsonIgnore].
cache[jsonPropertyInfo.NameAsString] = jsonPropertyInfo;
}
else if (other.PropertyInfo?.Name != jsonPropertyInfo.PropertyInfo?.Name &&
Copy link
Contributor

@layomia layomia Apr 13, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: we should add a comment here saying if the property names are equal, then the new keyword was used to override the property.

(jsonPropertyInfo.ShouldDeserialize == true || jsonPropertyInfo.ShouldSerialize == true))
{
// Check for name equality is required to determine when a new slot is used for the member.
// Therefore, if names are not the same, there is conflict due to the name policy or attributes.
ThrowHelper.ThrowInvalidOperationException_SerializerPropertyNameConflict(Type, jsonPropertyInfo);
}
// else ignore jsonPropertyInfo since it has [JsonIgnore] or it's hidden by a new slot.
}
// else ignore jsonPropertyInfo since it has [JsonIgnore].
}
}
}
Expand Down
Loading