[CSHARP-1728] EnumRepresentationConvention doesn't seem to apply during serialization of Dictionary<T, U>() Where T is an Enum Created: 27/Jul/16  Updated: 25/Sep/17  Resolved: 15/Jan/17

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

Type: Bug Priority: Major - P3
Reporter: Peter Garafano (Inactive) Assignee: Robert Stam
Resolution: Done Votes: 0
Labels: None
Remaining Estimate: Not Specified
Time Spent: Not Specified
Original Estimate: Not Specified

Issue Links:
Duplicate
is duplicated by CSHARP-2041 EnumRepresentationConvention does not... Closed

 Description   

Given the following example class:

public class Test
{
    public Dictionary<TestEnum, double> TestField { get; set; } = new Dictionary<TestEnum, double>();
}
 
public enum TestEnum
{
	A,
	B
}

If you register an EnumRepresentationConvention using the following code and try to insert an instance of the class

var pack = new ConventionPack { new EnumRepresentationConvention(BsonType.String) };
ConventionRegistry.Register("EnumStringConvention", pack, t => true);
var client = new MongoClient("mongodb://localhost/");
var db = client.GetDatabase("test");
var col = db.GetCollection<Test>("test");
await col.InsertOneAsync(new Test { TestField = { { TestEnum.A, 25d } } });

The last line throws a BsonSerializationException with the message:

When using DictionaryRepresentation.Document key values must serialize as strings.

If I alter the code to include the following line anywhere before the Insert.

BsonSerializer.RegisterSerializer(new EnumSerializer<TestEnum>(BsonType.String));

The insert succeeds and the resulting doc in the database looks like this:

{
        "_id" : ObjectId("5799282d945208a81ee8bfd2"),
        "TestField" : {
                "A" : 25
        }
}



 Comments   
Comment by Robert Stam [ 29/Jul/16 ]

Yes. It should compile. I copy and pasted that from a working test program.

What compile time error are you getting? Are you missing some using statements?

Comment by Peter Garafano (Inactive) [ 29/Jul/16 ]

Hi Robert,

The first workaround works best for me. I was able to apply it to Dictionary<ObjectId, double> as well. However as given it doesn't compile. It seems like it should be

var keySerializer = new EnumSerializer<TestEnum>(BsonType.String);
var valueSerializer = new DoubleSerializer();
var testSerializer = new DictionaryInterfaceImplementerSerializer<Dictionary<TestEnum, double>>(
    DictionaryRepresentation.Document, keySerializer, valueSerializer);
BsonSerializer.RegisterSerializer<Dictionary<TestEnum, double>>(testSerializer);

-Pete

Comment by Robert Stam [ 28/Jul/16 ]

If you wanted all enums serialized as strings whether they are dictionary keys or not you could write a custom serialization provider that handles enums. It would also apply to the case where the key of a dictionary is an enum.

It would look something like this:

public class EnumAsStringSerializationProvider : BsonSerializationProviderBase
{
    public override IBsonSerializer GetSerializer(Type type, IBsonSerializerRegistry serializerRegistry)
    {
        if (!type.IsEnum)
        {
            return null;
        }
 
        var enumSerializerType = typeof(EnumSerializer<>).MakeGenericType(type);
        var enumSerializerConstructor = enumSerializerType.GetConstructor(new[] { typeof(BsonType) });
        var enumSerializer = (IBsonSerializer)enumSerializerConstructor.Invoke(new object[] { BsonType.String });
 
        return enumSerializer;
    }
}

and you would register it like this at application startup:

var enumAsStringSerializationProvider = new EnumAsStringSerializationProvider();
BsonSerializer.RegisterSerializationProvider(enumAsStringSerializationProvider);

Comment by Robert Stam [ 28/Jul/16 ]

This does not surprise me.

EnumRepresentationConvention is a member convention, which means it applies to members of type TEnum, not values of type TEnum.

The type of the TestField member is not TEnum, so this convention does not apply.

Not sure what the best way to accomplish what you want here is. The easiest would be to register a properly configured serializer for Dictionary<TestEnum, double>:

var keySerializer = new EnumSerializer<TestEnum>(BsonType.String);
var valueSerializer = new DoubleSerializer();
var testSerializer = new DictionaryInterfaceImplementerSerializer<Dictionary<TestEnum, double>>(
    DictionaryRepresentation.Document, keySerializer, valueSerializer);
BsonSerializer.RegisterSerializer<Test>(testSerializer);

If you wanted this to apply automatically to all dictionaries whose keys are enums some other approach would be needed. Perhaps the easiest would be to write a custom serialization provider that handles dictionaries with keys that are enums.

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