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

Conversation

YohDeadfall
Copy link
Contributor

@YohDeadfall YohDeadfall commented Feb 11, 2020

Closes #32106.
Closes #30964.

Copy link
Contributor

@layomia layomia left a comment

Choose a reason for hiding this comment

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

Just a few more test cases to consider.

@@ -11,6 +11,231 @@ namespace System.Text.Json.Serialization.Tests
{
public static class PropertyVisibilityTests
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: can you use PascalCase for the tests names where possible and capitalize the first letter after an underscore where needed?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It's a naming used by EF Core for long test names, but am okay with changing this if you want, but first compare names.

Before: Serialize_base_public_property_on_conflict_with_derived_private
After: SerializeBasePublicPropertyOnConflictWithDerivedPrivate

The first one is more readable while the second looks like encoded data and is hard to read. So are you sure that pascal case is better? I can rename other tests and give them meaningful names instead of current (:

Copy link
Contributor

Choose a reason for hiding this comment

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

I do underscore-delimited pascal case chunks in such cases, e.g. Serialize_BasePublicProperty_OnConflictWithDerivedPrivate. The before pattern isn't used anywhere else in this codebase.

public string MyString { get; set; } = "DefaultValue";

[JsonPropertyName(nameof(MyString))]
internal string ConflictingString { get; set; } = "ConflictingValue";
Copy link
Contributor

Choose a reason for hiding this comment

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

We need a class variant where this property is public; and a test variant where the conflict spans across two inheritance levels.

@@ -11,6 +11,231 @@ namespace System.Text.Json.Serialization.Tests
{
public static class PropertyVisibilityTests
Copy link
Contributor

Choose a reason for hiding this comment

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

We need a couple more tests involving 3 levels of inheritance, particularly where we have collisions on public properties defined at different levels.

Copy link
Contributor

@layomia layomia left a comment

Choose a reason for hiding this comment

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

This PR should fix #30964 as well.

We need tests for the scenarios in #30964 (comment), particularly this case:

public class MyClass
{
    // This should be ignored.
    public int MyNumber { get; set; }
}
	
public class MyDerivedClass : MyClass
{
    // This should be (de)serialized.
    new public double MyNumber { get; set; }
}

@@ -11,6 +11,231 @@ namespace System.Text.Json.Serialization.Tests
{
public static class PropertyVisibilityTests
Copy link
Contributor

Choose a reason for hiding this comment

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

I do underscore-delimited pascal case chunks in such cases, e.g. Serialize_BasePublicProperty_OnConflictWithDerivedPrivate. The before pattern isn't used anywhere else in this codebase.

@layomia layomia added this to the 5.0 milestone Mar 13, 2020
@YohDeadfall
Copy link
Contributor Author

@layomia I finally found some time to finish this, but there is one question. Is there any reason to create a JsonProperyInfo for properties marked as ignored? Probably such members should be filtered out before creation JsonProperyInfos for them, or we could ignore suck infos and not to add them into a cache.

This will help with two things:

  • eliminate visibility checks causes by name collisions;
  • decrease memory usage.

If an member exists, then it's visible already (at least supports serialization or deserialization separately). If it's in a base class, we will overwrite it. Otherwise, throw an exception.

Copy link
Contributor

@layomia layomia left a comment

Choose a reason for hiding this comment

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

#32107 (review) is yet to be addressed.

@layomia
Copy link
Contributor

layomia commented Apr 11, 2020

Is there any reason to create a JsonProperyInfo for properties marked as ignored?

Creating a JsonPropertyInfo for ignored properties helps us distinguish between JSON to be ignored on deserialization, or JSON to be put in extension data (if available).

Perhaps we can remove instances where JsonPropertyInfo.ShouldSerialize is false from the serialization cache since we'll never serialize, but that should be considered outside of this PR.

@layomia
Copy link
Contributor

layomia commented Apr 13, 2020

@YohDeadfall
Copy link
Contributor Author

Thanks, saw this and fixed already (:

@YohDeadfall
Copy link
Contributor Author

@layomia tests fail, but for unrelated to this PR issue. Should I address it here?

@jozkee
Copy link
Member

jozkee commented Apr 30, 2020

It seems to me like the test failures are related to this PR since those doesn't fail on master, only here.

@YohDeadfall
Copy link
Contributor Author

The failing test should not be affected by this PR since it deserializes an object from an empty string. That area isn't touched, but will take a deeper look.

@jozkee
Copy link
Member

jozkee commented Apr 30, 2020

You removed the changes to JsonClassInfo recently introduced by #34675
b3e791d#diff-ba9769bf9ab1b28aebe3b65065b96b10
Probably when merging with master.

@YohDeadfall
Copy link
Contributor Author

@jozkee @layomia, I've fixed merge issues, so all tests pass.

Copy link
Contributor

@layomia layomia left a comment

Choose a reason for hiding this comment

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

Thanks a ton!

@layomia layomia merged commit 32120db into dotnet:master May 8, 2020
@YohDeadfall YohDeadfall deleted the fix-hidden-props branch May 17, 2020 18:22
@layomia layomia added the breaking-change Issue or PR that represents a breaking API or functional change over a prerelease. label May 23, 2020
@layomia
Copy link
Contributor

layomia commented May 23, 2020

This is a breaking change between .NET 3.1 Core/System.Text.Json 4.7.1 and .NET 5/System.Text.Json 5.0.0.

Rather than only observing properties visible from the type to be serialized, the serializer looks at the properties for all the base types in the inheritance chain. This means that InvalidOperationException could be thrown if there is a JSON property name conflict between inheritance levels, if the property higher in the inheritance chain was not hidden by the conflicting property in the more derived class (but by another property). A property is "hidden" if it is virtual and overridden in a derived class with polymorphism or with the new keyword.

The conflict would have to be fixed to avoid this exception. The most common way might be to place [JsonIgnore] on the conflicting property higher up in the inheritance chain.

@layomia
Copy link
Contributor

layomia commented May 28, 2020

Follow up: the breaking change this PR introduced was mitigated with #36936.

@danmoseley
Copy link
Member

@layomia this is labeled breaking change. Can you please open an issue if necessary with https://github.com/dotnet/docs/issues/new?template=dotnet-breaking-change.md

I don't see an existing one

@danmoseley danmoseley added the needs-breaking-change-doc-created Breaking changes need an issue opened with https://github.com/dotnet/docs/issues/new?template=dotnet label Jul 30, 2020
@layomia
Copy link
Contributor

layomia commented Oct 2, 2020

Removing the breaking-change labels from this PR since the breaking change it introduced (#32107 (comment)) has been mitigated/reversed in follow up PRs #36936 & #37720 to avoid breaking the OpenMU app from internal AppCompat runs - #37640.

@layomia layomia removed breaking-change Issue or PR that represents a breaking API or functional change over a prerelease. needs-breaking-change-doc-created Breaking changes need an issue opened with https://github.com/dotnet/docs/issues/new?template=dotnet labels Oct 2, 2020
@ghost ghost locked as resolved and limited conversation to collaborators Dec 10, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
6 participants