Skip to content

CSHARP-3494: Fix discriminator for generic types #1685

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

Open
wants to merge 8 commits into
base: main
Choose a base branch
from
Open
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
2 changes: 1 addition & 1 deletion src/MongoDB.Bson/Serialization/BsonClassMap.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1057,7 +1057,7 @@ public void Reset()
_creatorMaps.Clear();
_creator = null;
_declaredMemberMaps = new List<BsonMemberMap>();
_discriminator = _classType.Name;
_discriminator = BsonUtils.GetFriendlyTypeName(_classType);
Copy link
Contributor

Choose a reason for hiding this comment

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

This might fix it for BsonClassMap based serializers.

But shouldn't this be fixed more generally somehow?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

We had an offline discussion, and we decided it would make sense to keep this fix limited to BsonClassMap based serializers.

Copy link
Contributor

Choose a reason for hiding this comment

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

Do we need a new JIRA ticket for supporting discriminators for generic types when NOT using BsonClassMap?

_discriminatorIsRequired = false;
_extraElementsMemberMap = null;
_idMemberMap = null;
Expand Down
2 changes: 1 addition & 1 deletion tests/MongoDB.Bson.Tests/Jira/CSharp515Tests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ public S(IList<T> list)
}

private static readonly string __discriminatorAssemblyName = "MongoDB.Bson.Tests";
private string _jsonTemplate = ("{ '_id' : 1, 'R' : #V, 'S' : #V, 'RS' : { '_t' : 'S`1', '_v' : #V }, 'OR' : { '_t' : 'System.Collections.ObjectModel.ReadOnlyCollection`1[System.Int32]', '_v' : #V }, 'OS' : { '_t' : 'MongoDB.Bson.Tests.Jira.CSharp515.CSharp515Tests+S`1[System.Int32], " + __discriminatorAssemblyName + "', '_v' : #V } }").Replace("'", "\"");
private string _jsonTemplate = ("{ '_id' : 1, 'R' : #V, 'S' : #V, 'RS' : { '_t' : 'S<Int32>', '_v' : #V }, 'OR' : { '_t' : 'System.Collections.ObjectModel.ReadOnlyCollection`1[System.Int32]', '_v' : #V }, 'OS' : { '_t' : 'MongoDB.Bson.Tests.Jira.CSharp515.CSharp515Tests+S`1[System.Int32], " + __discriminatorAssemblyName + "', '_v' : #V } }").Replace("'", "\"");

[Fact]
public void TestNull()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@
* limitations under the License.
*/

using System.Collections.Generic;
using System.Linq;
using FluentAssertions;
using MongoDB.Bson.Serialization;
using MongoDB.Bson.Serialization.Attributes;
using MongoDB.Bson.TestHelpers;
Expand Down Expand Up @@ -65,6 +67,101 @@ private class H : G
{
}

// BaseDocument, BasedDocument2 and derived classes are used for tests with generic types
abstract class BaseDocument;

class DerivedDocument<T> : BaseDocument
{
[BsonId]
public int Id { get; set; }

public T Value { get; set; }
}

class DerivedDocumentDouble<T1, T2> : BaseDocument
{
[BsonId]
public int Id { get; set; }

public T1 Value1 { get; set; }

public T2 Value2 { get; set; }
}

//The deserialization tests for generic types needs to use a different set of classes than the serialization ones,
//otherwise the discriminators could have been already registered, depending on the order of the tests.
Copy link
Contributor

Choose a reason for hiding this comment

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

Why is this dependent on the order of the tests?

Configuration of classes/discriminators should work no matter what order the tests are run in.

//It's necessary to specify the derived specific types with BsonKnownTypes for this to work.
[BsonKnownTypes(typeof(DerivedDocument2<int>))]
[BsonKnownTypes(typeof(DerivedDocument2<List<Dictionary<string, int>>>))]
[BsonKnownTypes(typeof(DerivedDocumentDouble2<int, string>))]
abstract class BaseDocument2 {}

class DerivedDocument2<T> : BaseDocument2
{
[BsonId]
public int Id { get; set; }

public T Value { get; set; }
}

class DerivedDocumentDouble2<T1, T2> : BaseDocument2
{
[BsonId]
public int Id { get; set; }

public T1 Value1 { get; set; }

public T2 Value2 { get; set; }
}

[Fact]
public void TestDeserializeGenericType()
{
var serialized = """{ "_t" : "DerivedDocument2<Int32>", "_id" : 1, "Value" : 42 }""";
var rehydrated = BsonSerializer.Deserialize<BaseDocument2>(serialized);
rehydrated.Should().BeOfType<DerivedDocument2<int>>();
}

[Fact]
public void TestDeserializeGenericTypeWithNestedType()
{
var serialized = """{ "_t" : "DerivedDocument2<List<Dictionary<String, Int32>>>", "_id" : 1, "Value" : [{ "key" : 1 }] }""";
var rehydrated = BsonSerializer.Deserialize<BaseDocument2>(serialized);
rehydrated.Should().BeOfType<DerivedDocument2<List<Dictionary<string, int>>>>();
}

[Fact]
public void TestDeserializeGenericTypeWithTwoTypes()
{
var serialized = """{ "_t" : "DerivedDocumentDouble2<Int32, String>", "_id" : 1, "Value1" : 42, "Value2" : "hello" }""";
var rehydrated = BsonSerializer.Deserialize<BaseDocument2>(serialized);
rehydrated.Should().BeOfType<DerivedDocumentDouble2<int,string>>();
}

[Fact]
public void TestSerializeGenericType()
{
var document = new DerivedDocument<int> { Id = 1, Value = 42 };
var serialized = document.ToJson(typeof(BaseDocument));
serialized.Should().Be("""{ "_t" : "DerivedDocument<Int32>", "_id" : 1, "Value" : 42 }""");
}

[Fact]
public void TestSerializeGenericTypeWithNestedType()
{
var document = new DerivedDocument<List<Dictionary<string, int>>> { Id = 1, Value = [new() { { "key", 1 } }] };
var serialized = document.ToJson(typeof(BaseDocument));
serialized.Should().Be("""{ "_t" : "DerivedDocument<List<Dictionary<String, Int32>>>", "_id" : 1, "Value" : [{ "key" : 1 }] }""");
}

[Fact]
public void TestSerializeGenericTypeWithTwoTypes()
{
var document = new DerivedDocumentDouble<int, string> { Id = 1, Value1 = 42, Value2 = "hello"};
var serialized = document.ToJson(typeof(BaseDocument));
serialized.Should().Be("""{ "_t" : "DerivedDocumentDouble<Int32, String>", "_id" : 1, "Value1" : 42, "Value2" : "hello" }""");
}

[Fact]
public void TestSerializeObjectasObject()
{
Expand Down