[CSHARP-2549] Shadowing/Hidden properties causing BsonSerializationException Created: 15/Mar/19  Updated: 27/Oct/23  Resolved: 19/Mar/19

Status: Closed
Project: C# Driver
Component/s: Serialization
Affects Version/s: 2.7.3, 2.8.0
Fix Version/s: None

Type: Bug Priority: Major - P3
Reporter: Gabriel Fetz Assignee: Robert Stam
Resolution: Works as Designed Votes: 0
Labels: None
Remaining Estimate: Not Specified
Time Spent: Not Specified
Original Estimate: Not Specified
Environment:

Windows 10 Professional x64, .NET Core 2.2


Issue Links:
Duplicate
duplicates CSHARP-2541 when using inherentence of classes an... Closed

 Description   

Hi guys.
Our project uses shadowing/hiding on inherited classes to keep backwards compatibility with older layouts of received Json files. Here are some simple example classes that reproduce the problem:

namespace Base
{
    public class Root
    {
        public string Id {get; set;}
        public SecondClass SecondClass {get; set;}
    }
}

namespace Base
{
    public class SecondClass
    {
        public int Number {get; set;}        
    }
}

namespace NewVersion
{
    public class Root : Base.Root
    {
        public new SecondClass SecondClass {get; set;}        
    }
}

namespace NewVersion
{
    public class SecondClass : Base.SecondClass 
    {
        public string NewProperty {get; set;}        
    }
}

When I call the GetCollection method and set the type as NewVersion.Root, I'm getting a "BsonSerializationException".

db.GetCollection<NewVersion.Root>(typeof(NewVersion.Root).Name);

"MongoDB.Bson.BsonSerializationException: 'The property 'SecondClass' of type 'NewVersion.Root' cannot use element name 'SecondClass' because it is already being used by property 'SecondClass' of type 'Base.Root'.'"

I've tried to map the classes on project startup, and the only way I found to make everything working fine is using the "SetElementName" method to change the property name to an exclusive one, however, as a side effect, these altered names will be saved on the database (as expected).
As a workaround, I'm converting all the created objects to an ExpandoObject, removing all hidden properties, then I convert this ExpandoObject to a BsonDocument. In this case, it's not possible to use LINQ queries because the "GetCollection" method is allways typed as BsonDocument.

I'm not sure if this is a bug or not.
Any suggestions?
Thanks in advance!



 Comments   
Comment by Brian Buvinghausen [ 24/Apr/19 ]

Is there a clean/elegant way to have JSON.NET perform the BSON serialization and deserialization?

Comment by Brian Buvinghausen [ 24/Apr/19 ]

Robert I would expect this to perform the way JSON.NET performs which is it simply ignores the hidden properties.   We currently use a base object on our API with every property typed as a string so that we don't have any model binder exception messages from failed parses.  From there we have an interim class that then uses the new keyword to hide the string property with it's strongly typed definition then we use AutoMapper to map from the post class to the interim class.  It would be nice to be able to send that class to the BSON serializer and not have it blow sky high because of duplicate names.  I absolutely 100% don't care about the string equivalent I only care about the post auto mapped strongly typed values but having inheritance and using the new keyword is nice because it forces the names to be exact matches so that everything works as designed in auto mapper and I only have to configure the various maps from strings to strongly typed values.  I'm curious where this sentiment of shadowing/hiding is not considered best practice.  Why did they give us the language feature to use if it should not be used.  If this were a problem why does JSON.NET support the function exactly as expected yet this driver does not?

Comment by Robert Stam [ 19/Mar/19 ]

The automatic serialization support (that uses the BsonClassMapSerializer) is written assuming that subclasses don't shadow or hide inherited properties, so that's a scenario that we intentionally do not support.

One could argue that shadowing/hiding inherited properties is not best practice.

If you wish to support classes like this you would have to write your own custom serializer. As part of that work you would have to decide how to handle the conflicting properties with the same name. Which one do you serialize? If both, what names should the fields have since they can't be the same?

Generated at Wed Feb 07 21:42:52 UTC 2024 using Jira 9.7.1#970001-sha1:2222b88b221c4928ef0de3161136cc90c8356a66.