Uploaded image for project: 'C# Driver'
  1. C# Driver
  2. CSHARP-2132

"Duplicate element name '...'" when serializing a derived type with a discriminator

    • Type: Icon: Task Task
    • Resolution: Works as Designed
    • Priority: Icon: Minor - P4 Minor - P4
    • None
    • Affects Version/s: 2.5
    • Component/s: BSON, Serialization
    • Labels:
    • Minor Change

      When using a custom property for a discriminator value, there is a bug that occurs when serializing the object. The code below reproduces the error:

      [BsonKnownTypes(typeof(ChildClass1))]
      public abstract class ParentClass
      {
      	public string Type { get; set; }
      }
      
      [BsonDiscriminator("Child1")]
      public class ChildClass1 : ParentClass
      {
      	public ChildClass1()
      	{
      		Type = "Child1";
      	}
      }
      
      public class ParentClassDiscriminator : IDiscriminatorConvention
      {
      	public string ElementName { get; } = "Type";
      
      	public Type GetActualType(IBsonReader bsonReader, Type nominalType)
      	{
      		var bookmark = bsonReader.GetBookmark();
      
      		bsonReader.ReadStartDocument();
      		if (!bsonReader.FindElement(ElementName))
      		{
      			throw new NotSupportedException($"Could not find element named: {ElementName}");
      		}
      
      		try
      		{
      			var val = bsonReader.ReadString();
      
      
      			switch (val ?? "")
      			{
      				case "Child1":
      					return typeof(ChildClass1);
      				default:
      					throw new NotSupportedException($"Could not find a Type to match type of: {val}");
      			}
      		}
      		finally
      		{
      			bsonReader.ReturnToBookmark(bookmark);
      		}
      	}
      
      	public BsonValue GetDiscriminator(Type nominalType, Type actualType)
      	{
      		if (actualType == typeof(ChildClass1))
      		{
      			return "Child1";
      		}
      
      		throw new NotImplementedException();
      	}
      }
      public static class Bug
      {
      	public static void Example()
      	{
      		BsonSerializer.RegisterDiscriminatorConvention(typeof(ParentClass), new ParentClassDiscriminator());
      
      		//case 1
      		var object1 = new ChildClass1();
      		object1.ToBsonDocument();//does not throw an error
      
      		//case 2
      		ParentClass object2 = new ChildClass1();
      		object2.ToBsonDocument();//throws InvalidOperationException("Duplicate element name 'Type'.")
      	}
      }
      

      In case case 1 this does not cause an issue since this line evaluates as false. However, in case 2 the evaluation is true which causes the serializer to write the discriminator value before continuing to serialize the rest of the object.

      The solution should be to change the "SerializeDiscriminator" method to:

      private void SerializeDiscriminator(BsonSerializationContext context, Type nominalType, object obj, List<BsonMemberMap> remainingMemberMaps)
      {
      	var discriminatorConvention = _classMap.GetDiscriminatorConvention();
      	if (discriminatorConvention != null)
      	{
      		var actualType = obj.GetType();
      		var discriminator = discriminatorConvention.GetDiscriminator(nominalType, actualType);
      		if (discriminator != null)
      		{
      			context.Writer.WriteName(discriminatorConvention.ElementName);
      			BsonValueSerializer.Instance.Serialize(context, discriminator);
      
      			var memberMap = remainingMemberMaps.FirstOrDefault(
      				bsonMemberMap => bsonMemberMap.ElementName == discriminatorConvention.ElementName);
      			if (memberMap != null)
      			{
      				remainingMemberMaps.Remove(memberMap);
      			}
      		}
      	}
      }
      

      Also change line 599 to:

      SerializeDiscriminator(context, args.NominalType, document, remainingMemberMaps);
      

            Assignee:
            robert@mongodb.com Robert Stam
            Reporter:
            cy.a.scott Cy
            Votes:
            0 Vote for this issue
            Watchers:
            2 Start watching this issue

              Created:
              Updated:
              Resolved: