[CSHARP-1165] Dictionary can't be serialized as a document Created: 19/Jan/15  Updated: 05/Apr/16  Resolved: 04/Apr/15

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

Type: Bug Priority: Minor - P4
Reporter: Bar Arnon Assignee: Unassigned
Resolution: Done Votes: 0
Labels: None
Remaining Estimate: Not Specified
Time Spent: Not Specified
Original Estimate: Not Specified


 Description   

In the new version, dictionaries are serialized (unless specified otherwise) as a document which isn't backwards compatible in some cases, for example this class:

    public class Hamster
    {
        public ObjectId Id { get; private set; }
        public Dictionary<IPAddress, string> Dictionary { get; private set; }
        public Hamster()
        {
            Id = ObjectId.GenerateNewId();
            Dictionary = new Dictionary<IPAddress, string>();
            Dictionary[IPAddress.Parse("8.8.8.8")] = "";
        }
    }

In v1.9 `Dictionary` is serialized just fine as a an array of arrays, but in v2.0 serializing this would throw the following exception:

MongoDB.Bson.BsonSerializationException: Element name '8.8.8.8' is not valid.

This can be solved by adding an attribute:

[BsonDictionaryOptions(Representation = DictionaryRepresentation.ArrayOfArrays)]
public Dictionary<IPAddress, string> Dictionary { get; private set; }

But as far as I know this needs to be done on a case by case basis as there isn't a relevant convention pack



 Comments   
Comment by Bar Arnon [ 20/Jan/15 ]

This solves the issue very nicely, thank you.
I've changed the convention a bit to solve this issue as well.

Comment by Robert Stam [ 20/Jan/15 ]

We can make the custom convention a little more sophisticated so that it drills down into the ImpliedImplementationInterfaceSerializer and applies the DictionaryRepresentation there. This can be done in a more general fashion by noticing that the ImpliedImplementationInterfaceSerializer implements IChildSerializerConfigurable, which is an interface that represents the general case of wanting to forward serialization configuration items (usually attributes) to child serializers when they don't apply to the parent serializer.

Here's a version that works with interfaces also:

public class DictionaryRepresentationConvention : ConventionBase, IMemberMapConvention
{
    private readonly DictionaryRepresentation _representation;
 
    public DictionaryRepresentationConvention(DictionaryRepresentation representation)
    {
        _representation = representation;
    }
 
    public void Apply(BsonMemberMap memberMap)
    {
        var serializer = memberMap.GetSerializer();
        var reconfiguredSerializer = Apply(serializer);
        if (reconfiguredSerializer != null)
        {
            memberMap.SetSerializer(reconfiguredSerializer);
        }
    }
 
    private IBsonSerializer Apply(IBsonSerializer serializer)
    {
        var configurableSerializer = serializer as IDictionaryRepresentationConfigurable;
        if (configurableSerializer != null)
        {
            return configurableSerializer.WithDictionaryRepresentation(_representation);
        }
 
        var childConfigurableSerializer = serializer as IChildSerializerConfigurable;
        if (childConfigurableSerializer != null)
        {
            var reconfiguredChildSerializer = Apply(childConfigurableSerializer.ChildSerializer);
            if (reconfiguredChildSerializer != null)
            {
                return childConfigurableSerializer.WithChildSerializer(reconfiguredChildSerializer);
            }
        }
 
        return null;
    }
}

Comment by Bar Arnon [ 20/Jan/15 ]

Hey, I already went ahead and implemented such a convention, however in our production code (where we don't have any hamsters) we use the interface in public properties.
And while Dictionary is serialized by DictionaryInterfaceImplementerSerializer which implements IDictionaryRepresentationConfigurable, IDictionary is serialized by ImpliedImplementationInterfaceSerializer which doesn't.

So, the convention works for Dictionary<DateTime,string> but not for IDictionary<DateTime,string>


I use DateTime because some IP addresses (e.g. 8.8.8.8) apparently can be serialized as a name but DateTime always can't.

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