C# MongoDB How to Deserialize to ImmutableList<T>

Viewed 48

I just stumbled over this - I registered my Records which include some ImmutableLists.

Basically, like this:

record A {
   [BsonId]
   string Id {get; init;}
   string Name {get; init;}
   ImmutableList<string> Properties {get; init;}
};

record B {
   [BsonId]
   string Id {get; init;}
   string Name {get; init;}
   ImmutableList<A> Members {get; init;}
}

Now, serializing those to my MongoDB works fine, everything looks like it should. However, the deserialization back in to the Records does not work, from the exception thrown it seems that the Add method does not quite work the way the deserializer expects (well, sure, instead of adding to the list itself it instead returns a new list with the added item).

Now, I found how to write a basic deserializer myself - but the nested A in B makes it a bit painful and it's quite brittle (e.g. if I later add additional nullable fields). What would be the best way to approach this problem?

1 Answers

This improvement raised with MongoDB explains the behavior and a possible solution by creating a custom serializer just for ImmutableList<T>:

public class ImmutableListSerializer<TValue> : 
 EnumerableInterfaceImplementerSerializerBase<ImmutableList<TValue>, TValue> 
{
  protected override object CreateAccumulator() =>
    ImmutableList.CreateBuilder<TValue>();
  protected override ImmutableList<TValue> FinalizeResult(object accumulator) =>
    ((ImmutableList<TValue>.Builder)accumulator).ToImmutable();
 
}

You can register this serializer for the required properties either on the properties themselves:

[BsonSerializer(typeof(ImmutableListSerializer<string>))]
public ImmutableList<string> Properties { get; init; }

// ...

[BsonSerializer(typeof(ImmutableListSerializer<A>))]
public ImmutableList<A> Members { get; init; }

Alternatively, you can register it globally when initializing the application:

BsonSerializer.RegisterSerializer(
    typeof(ImmutableList<A>), 
    new ImmutableListSerializer<A>());
BsonSerializer.RegisterSerializer(
    typeof(ImmutableList<string>),
    new ImmutableListSerializer<string>());

The downside is that you need to do this for each list type you need, but there is hope that the driver will support this sooner or later.

Related