MongoDB反序列化:混合类型字段转对象数组报错排查
有一个混合类型的字段(可为单个Association对象或Association对象数组),尝试通过自定义序列化器将其反序列化为Association[],代码如下:
class AssociationSerializer : SerializerBase<Association[]> { public override Association[] Deserialize(BsonDeserializationContext context, BsonDeserializationArgs args) { var type = context.Reader.GetCurrentBsonType(); switch (type) { case BsonType.Array: var arraySerializer = new ArraySerializer<Association>(); return arraySerializer.Deserialize(context, new BsonDeserializationArgs()); case BsonType.Document: var bsonDocument = context.Reader.ToBsonDocument(); var association= BsonSerializer.Deserialize<Association>(bsonDocument); return new[] { association }; default: throw new NotSupportedException(); } } }
运行时抛出错误:
System.InvalidOperationException: ReadBsonType can only be called when State is Type, not when State is Value.
at MongoDB.Bson.IO.BsonReader.ThrowInvalidState(String methodName, BsonReaderState[] validStates)
at MongoDB.Bson.IO.BsonBinaryReader.ReadBsonType()
at MongoDB.Bson.Serialization.BsonClassMapSerializer1.DeserializeClass(BsonDeserializationContext context) at MongoDB.Bson.Serialization.BsonClassMapSerializer1.Deserialize(BsonDeserializationContext context, BsonDeserializationArgs args)
at MongoDB.Bson.Serialization.IBsonSerializerExtensions.Deserialize[TValue](IBsonSerializer`1 serializer, BsonDeserializationContext context)
相关实体类:
public class Association { public string Id { get; set; } public int Type { get; set; } }
JSON示例:
{ "association" : [ { "_id" : "b8e0a770-951b-5e85-84a6-8ca8cd0998e0", "type" : 0 } ] } { "association" : { "_id" : "08dc1cb0-0f88-4dcf-70db-ff3b8c000003" } }
错误原因
问题出在处理BsonType.Document的分支:调用context.Reader.ToBsonDocument()会将Bson阅读器的指针移动到整个文档末尾,后续父序列化器尝试继续读取时,阅读器状态处于Value而非预期的Type,触发状态异常。
修正后的序列化器代码
class AssociationSerializer : SerializerBase<Association[]> { public override Association[] Deserialize(BsonDeserializationContext context, BsonDeserializationArgs args) { var reader = context.Reader; var currentType = reader.GetCurrentBsonType(); switch (currentType) { case BsonType.Array: var arraySerializer = new ArraySerializer<Association>(); return arraySerializer.Deserialize(context, args); case BsonType.Document: // 直接从当前阅读器位置反序列化单个对象,避免指针移动到无效位置 var singleAssociation = BsonSerializer.Deserialize<Association>(reader); return new[] { singleAssociation }; default: throw new NotSupportedException($"不支持的Bson类型:{currentType}"); } } }
额外注意事项
需确保序列化器正确关联到目标字段:
- 方式一:在实体类字段上标记特性:
public class ParentEntity { [BsonSerializer(typeof(AssociationSerializer))] public Association[] Association { get; set; } } - 方式二:通过
BsonClassMap注册:BsonClassMap.RegisterClassMap<ParentEntity>(cm => { cm.AutoMap(); cm.GetMemberMap(x => x.Association) .SetSerializer(new AssociationSerializer()); });
内容的提问来源于stack exchange,提问作者Sameer

