MongoDb C# driver - serializing List < enum>as string[]

1cosmwyk  于 2023-08-04  发布在  Go
关注(0)|答案(3)|浏览(104)

我有一个属性类型为List<SomeEnum>的类。大概是这样的:

public enum MyEnum
{
  A,
  B
}

public class MyClass
{
    public string Id { get; set; }
    public List<MyEnum> Values { get; set; }
}

字符串
我已经在用EnumRepresentationConvention了:

ConventionRegistry.Register("EnumStringConvention", new ConventionPack { new EnumRepresentationConvention(BsonType.String) }, t => true);


尽管如此,Values属性还是被序列化为一个int数组(简单的枚举属性可以正确地作为int处理)。在列表序列化的上下文中似乎没有使用该约定。
我如何强制序列化程序写字符串而不是整型?

b4lqfgs4

b4lqfgs41#

不调用ConventionRegistry.Register(),而是将数据注解[BsonRepresentation(BsonType.String)]添加到MyClass的属性Values

public class MyClass
{
    public string Id { get; set; }

    [BsonRepresentation(BsonType.String)] 
    public List<MyEnum> Values { get; set; }
}

字符串
修改后,collection.InsertOne(obj);保存了以下内容:

{
    "_id" : "1",
    "Values" : [ 
        "A", 
        "B"
    ]
}

pkln4tw6

pkln4tw62#

唉,这个公关已经关闭,因为不会修复。https://github.com/mongodb/mongo-csharp-driver/pull/305#issuecomment-731475503。
这是有充分理由的,但我认为这是人们想要做的事情,所以根据你的胃口,你可以试试这个(.NET 5):

using System;
using System.Collections.Generic;
using System.Reflection;

using MongoDB.Bson.Serialization;
using MongoDB.Bson.Serialization.Attributes;

public sealed class EnumWrapper<TEnum>
    where TEnum : struct, Enum
{
    [BsonConstructor]
    public EnumWrapper(TEnum value) => this.Value = value;

    public TEnum Value { get; }

    public static readonly IBsonSerializer<EnumWrapper<TEnum>> Serializer = new BsonSerializerImpl();

    public static implicit operator TEnum(EnumWrapper<TEnum> wrapper) => wrapper.Value;

    public static implicit operator EnumWrapper<TEnum>(TEnum value) => new(value);

    public override bool Equals(object obj) =>
        obj is EnumWrapper<TEnum> wrapper
        && EqualityComparer<TEnum>.Default.Equals(this.Value, wrapper.Value);

    public override int GetHashCode() => HashCode.Combine(this.Value);

    public override string ToString() => this.Value.ToString();

    private class BsonSerializerImpl : IBsonSerializer<EnumWrapper<TEnum>>
    {
        public Type ValueType => typeof(EnumWrapper<TEnum>);

        public void Serialize(BsonSerializationContext context, BsonSerializationArgs args, EnumWrapper<TEnum> value) =>
            context.Writer.WriteString(((TEnum)value).ToString());

        public void Serialize(BsonSerializationContext context, BsonSerializationArgs args, object value) =>
            this.Serialize(context, args, (EnumWrapper<TEnum>)value);

        object IBsonSerializer.Deserialize(BsonDeserializationContext context, BsonDeserializationArgs args) =>
            this.Deserialize(context, args);

        public EnumWrapper<TEnum> Deserialize(BsonDeserializationContext context, BsonDeserializationArgs args) =>
            Enum.Parse<TEnum>(context.Reader.ReadString());
    }
}

public class EnumWrapperBsonSerializationProvider : IBsonSerializationProvider
{
    public IBsonSerializer GetSerializer(Type type)
    {
        if (!type.IsGenericType)
        {
            return null;
        }

        var typeDefinition = type.GetGenericTypeDefinition();
        if (typeDefinition != typeof(EnumWrapper<>))
        {
            return null;
        }

        var field = type.GetField(nameof(EnumWrapper<Hack>.Serializer), BindingFlags.Public | BindingFlags.Static);
        return (IBsonSerializer)field.GetValue(null);
    }

    private enum Hack { }
}

字符串
现在,您可以在通常使用TEnum的任何地方使用EnumWrapper<TEnum>,并且它将执行您想要的操作。如果你没有注册序列化提供程序,它将序列化为嵌套对象,所以在做任何事情之前,你应该调用这个:

BsonSerializer.RegisterSerializationProvider(new EnumWrapperBsonSerializationProvider());

fxnxkyjh

fxnxkyjh3#

这段代码解决了将枚举集合序列化为字符串数组的问题,看起来很简单,但花了一段时间才弄清楚:
呼叫

BsonSerializer.RegisterSerializationProvider(new EnumProvider());

字符串
在任何其他代码BSON代码之前,其中EnumProvider是:

class EnumProvider : IBsonSerializationProvider
{
    public static IBsonSerializer CreateClass<T>() where T : struct, Enum => new EnumSerializer<T>(BsonType.String);

    private static readonly MethodInfo s_mi = typeof(EnumProvider).GetMethod(nameof(CreateClass));
    public IBsonSerializer GetSerializer(Type type)
        => type.IsEnum ? (IBsonSerializer)s_mi.MakeGenericMethod(type).Invoke(null, null) : null;
}


不要担心在这里使用反射的性能,这段代码对每个枚举类型调用一次

相关问题