在MongoDB中自定义序列化枚举
本文关键字:序列化 枚举 自定义 MongoDB | 更新日期: 2023-09-27 18:05:36
引用这个关于Json中字符串到枚举的自定义序列化的SO问题。. NET,装饰枚举成员使用EnumMember属性-有办法让MongoDB执行相同的壮举?
我刚刚重构了一些以前的字符串字段到枚举,并且想知道是否有任何方法可以指示Mongo在(反)序列化时也读取EnumMember值,并避免我必须通过数据库并更新所有当前文本值。
我正在使用package: packagerreference Include="MongoDB.Bson"Version ="2.12.1"
my map class:
public class OfferMap
{
public static void Configure()
{
BsonClassMap.RegisterClassMap<Offer>(map => //Offer is a class
{
map.AutoMap();
map.SetIgnoreExtraElements(true);
map
.SetIsRootClass(true);
map
.MapMember(x => x.OfferType)
.SetSerializer(new EnumSerializer<OfferType>(MongoDB.Bson.BsonType.String)) // OfferType is an Enum
.SetElementName("offerType")
.SetIgnoreIfNull(false)
.SetIsRequired(true);
我使用CustomEnumSerializer来处理EnumMember属性
public class CustomEnumSerializer<TEnum> : StructSerializerBase<TEnum>, IRepresentationConfigurable<CustomEnumSerializer<TEnum>> where TEnum : struct
{
private static readonly Dictionary<Type, Dictionary<string, object>> _fromValueMap = new Dictionary<Type, Dictionary<string, object>>(); // string representation to Enum value map
private static readonly Dictionary<Type, Dictionary<object, string>> _toValueMap = new Dictionary<Type, Dictionary<object, string>>(); // Enum value to string map
// private fields
private readonly BsonType _representation;
// constructors
/// <summary>
/// Initializes a new instance of the <see cref="EnumSerializer{TEnum}"/> class.
/// </summary>
public CustomEnumSerializer()
: this((BsonType)0) // 0 means use underlying type
{
}
/// <summary>
/// Initializes a new instance of the <see cref="EnumSerializer{TEnum}"/> class.
/// </summary>
/// <param name="representation">The representation.</param>
public CustomEnumSerializer(BsonType representation)
{
switch (representation)
{
case 0:
case BsonType.Int32:
case BsonType.Int64:
case BsonType.String:
break;
default:
var message = string.Format("{0} is not a valid representation for an EnumSerializer.", representation);
throw new ArgumentException(message);
}
// don't know of a way to enforce this at compile time
var enumTypeInfo = typeof(TEnum).GetTypeInfo();
if (!enumTypeInfo.IsEnum)
{
var message = string.Format("{0} is not an enum type.", typeof(TEnum).FullName);
throw new BsonSerializationException(message);
}
_representation = representation;
if (representation == BsonType.String)
{
var enumType = typeof(TEnum);
if (!_fromValueMap.ContainsKey(enumType))
{
Dictionary<string, object> fromMap = new Dictionary<string, object>(StringComparer.InvariantCultureIgnoreCase);
Dictionary<object, string> toMap = new Dictionary<object, string>();
FieldInfo[] fields = enumType.GetFields(BindingFlags.Static | BindingFlags.Public);
foreach (FieldInfo field in fields)
{
string name = field.Name;
object enumValue = Enum.Parse(enumType, name);
// use EnumMember attribute if exists
EnumMemberAttribute enumMemberAttrbiute = field.GetCustomAttribute<EnumMemberAttribute>();
if (enumMemberAttrbiute != null)
{
string enumMemberValue = enumMemberAttrbiute.Value;
fromMap[enumMemberValue] = enumValue;
toMap[enumValue] = enumMemberValue;
}
else
{
toMap[enumValue] = name;
}
fromMap[name] = enumValue;
}
_fromValueMap[enumType] = fromMap;
_toValueMap[enumType] = toMap;
}
}
}
// public properties
/// <summary>
/// Gets the representation.
/// </summary>
/// <value>
/// The representation.
/// </value>
public BsonType Representation
{
get { return _representation; }
}
// public methods
/// <summary>
/// Deserializes a value.
/// </summary>
/// <param name="context">The deserialization context.</param>
/// <param name="args">The deserialization args.</param>
/// <returns>A deserialized value.</returns>
public override TEnum Deserialize(BsonDeserializationContext context, BsonDeserializationArgs args)
{
var bsonReader = context.Reader;
var bsonType = bsonReader.GetCurrentBsonType();
switch (bsonType)
{
case BsonType.Int32: return (TEnum)Enum.ToObject(typeof(TEnum), bsonReader.ReadInt32());
case BsonType.Int64: return (TEnum)Enum.ToObject(typeof(TEnum), bsonReader.ReadInt64());
case BsonType.Double: return (TEnum)Enum.ToObject(typeof(TEnum), (long)bsonReader.ReadDouble());
case BsonType.String:
var fromValue = FromValue(typeof(TEnum), bsonReader.ReadString());
return (TEnum)Enum.Parse(typeof(TEnum), fromValue.ToString());
default:
throw CreateCannotDeserializeFromBsonTypeException(bsonType);
}
}
/// <summary>
/// Serializes a value.
/// </summary>
/// <param name="context">The serialization context.</param>
/// <param name="args">The serialization args.</param>
/// <param name="value">The object.</param>
public override void Serialize(BsonSerializationContext context, BsonSerializationArgs args, TEnum value)
{
var bsonWriter = context.Writer;
switch (_representation)
{
case 0:
var underlyingTypeCode = Type.GetTypeCode(Enum.GetUnderlyingType(typeof(TEnum)));
if (underlyingTypeCode == TypeCode.Int64 || underlyingTypeCode == TypeCode.UInt64)
{
goto case BsonType.Int64;
}
else
{
goto case BsonType.Int32;
}
case BsonType.Int32:
bsonWriter.WriteInt32(Convert.ToInt32(value));
break;
case BsonType.Int64:
bsonWriter.WriteInt64(Convert.ToInt64(value));
break;
case BsonType.String:
var val = ToValue(typeof(TEnum), value);
bsonWriter.WriteString(val);
break;
default:
throw new BsonInternalException("Unexpected EnumRepresentation.");
}
}
private string ToValue(Type enumType, object obj)
{
Dictionary<object, string> map = _toValueMap[enumType];
return map[obj];
}
private object FromValue(Type enumType, string value)
{
Dictionary<string, object> map = _fromValueMap[enumType];
if (!map.ContainsKey(value))
return value;
return map[value];
}
/// <summary>
/// Returns a serializer that has been reconfigured with the specified representation.
/// </summary>
/// <param name="representation">The representation.</param>
/// <returns>The reconfigured serializer.</returns>
public CustomEnumSerializer<TEnum> WithRepresentation(BsonType representation)
{
if (representation == _representation)
{
return this;
}
else
{
return new CustomEnumSerializer<TEnum>(representation);
}
}
// explicit interface implementations
IBsonSerializer IRepresentationConfigurable.WithRepresentation(BsonType representation)
{
return WithRepresentation(representation);
}
}
我需要一个自定义的反序列化器,当遇到数据中的意外值时返回默认值,而不是抛出反序列化异常的默认行为。
public class CustomEnumSerializer<TEnum>: MongoDB.Bson.Serialization.Serializers.EnumSerializer<TEnum>
where TEnum : struct, IComparable, IFormattable, IConvertible
{
public override TEnum Deserialize(BsonDeserializationContext context, BsonDeserializationArgs args)
{
var bsonReader = context.Reader;
var bsonType = bsonReader.GetCurrentBsonType();
var val = "";
switch (bsonType)
{
case BsonType.String:
val = bsonReader.ReadString() ?? "";
break;
case BsonType.Int32:
val = bsonReader.ReadInt32().ToString();
break;
case BsonType.Int64:
val = bsonReader.ReadInt64().ToString();
break;
case BsonType.Null:
return default(TEnum);
default:
return base.Deserialize(context, args);
}
if(Enum.TryParse(val, true, out TEnum result)){
return result;
}
return default(TEnum);
}
}
在您的存储库中实现它:
static MyRepository()
{
BsonClassMap.RegisterClassMap<MyDataType>(ms =>
{
ms.AutoMap();
ms.GetMemberMap(i => i.MyEnum)
.SetSerializer(new CustomEnumSerializer<MyEnumType>());
});
}