【问题标题】:mongodb C# exception Cannot deserialize string from BsonType Int32mongodb C#异常无法从BsonType Int32反序列化字符串
【发布时间】:2013-11-08 23:20:17
【问题描述】:

我是在 C# 中使用 mongo db 的新手,但我正在尝试在 mongo db 中导入大型数据库。 MyDb 由仅具有简单参数 Id 、 Body 、 Title Tags 的实体组成。

这是 mongo 中的实体示例。

{
"Id" : "someff asdsa",
"Title" : "fsfds fds",
"Body ": "fsdfsd fs",
"Tags" : "fsdfdsfsd"
}

这是我在 C# 中的 mongoEntity 类

 [BsonIgnoreExtraElements]
    class Element
    {
        [BsonId]
        public ObjectId _id { get; set; }
        [BsonElement("Id")]
        public string Id { get; set; }
        [BsonElement("Title")]
        public string Title { get; set; }
        [BsonElement("Body")]
        public string Body { get; set; }
        [BsonElement("Tags")]
        public string Tags { get; set; }

        public void ShowOnConsole()
        {
            Console.WriteLine(" _id {0} Id {1} Title {2} Body {3} Tags {4} ", _id, Id, Title, Body, Tags);
        }

    }

这是我在 Main 方法中的代码

  const string connectionString = "mongodb://localhost";
            var client = new MongoClient(connectionString);

            MongoServer server = client.GetServer();
            MongoDatabase database = server.GetDatabase("mydb");


            MongoCollection<Element> collection = database.GetCollection<Element>("train");
            Console.WriteLine("Zaimportowano {0} rekordow ", collection.Count());

            MongoCursor<Element> ids = collection.FindAll();   

             foreach (Element entity in ids)
             {
                 entity.ShowOnConsole();
             }

当我运行这段代码时,我可以看到一些数据,但我遇到了异常 “无法从 BsonType Int32 反序列化字符串。” 我认为其中一个属性在数据库中表示为 int ,但我不知道如何处理它?为什么一个实体中的一个属性是 int 而另一个对象中的相同属性是 string ? 我必须做什么才能读取所有数据库?

【问题讨论】:

    标签: c# mongodb


    【解决方案1】:

    是的,C# 对象中的 String 属性在 mongo 存储中具有 Int32 值,因此在序列化过程中出现异常(参见 MongoDB.Bson.Serialization.Serializers.BsonStringSerializer 类的代码)。

    1) 您可以定义自己的序列化程序,它将Int32 值反序列化为字符串属性以及String 值。这里是:

    public sealed class StringOrInt32Serializer : BsonBaseSerializer
    {
        public override object Deserialize(BsonReader bsonReader, Type nominalType,
            Type actualType, IBsonSerializationOptions options)
        {
            var bsonType = bsonReader.CurrentBsonType;
            switch (bsonType)
            {
                case BsonType.Null:
                    bsonReader.ReadNull();
                    return null;
                case BsonType.String:
                    return bsonReader.ReadString();
                case BsonType.Int32:
                    return bsonReader.ReadInt32().ToString(CultureInfo.InvariantCulture);
                default:
                    var message = string.Format("Cannot deserialize BsonString or BsonInt32 from BsonType {0}.", bsonType);
                    throw new BsonSerializationException(message);
            }
        }
    
        public override void Serialize(BsonWriter bsonWriter, Type nominalType,
            object value, IBsonSerializationOptions options)
        {
            if (value != null)
            {
                bsonWriter.WriteString(value.ToString());
            }
            else
            {
                bsonWriter.WriteNull();
            }
        }
    }
    

    然后用这个序列化器标记必要的属性(你认为在 MongoDB 中有不同的类型),例如:

    [BsonElement("Body")]
    [BsonSerializer(typeof(StringOrInt32Serializer))]
    public string Body { get; set; }
    

    我也在这里发现了非常相似的问题:Deserializing field when type is changed using MongoDb csharp driver


    2) 第二种方法 - '规范化' 存储中的数据:将所有整数字段值转换为字符串。因此,您应该将字段 $type 从 16(32 位整数)更改为 2(字符串)。见BSON types。让我们为body 字段:

    db.train.find({ 'body' : { $type : 16 } }).forEach(function (element) {   
      element.body = "" + element.body;  // Convert field to string
      db.train.save(element);
    });
    

    【讨论】:

    • 非常感谢您的回复!
    • 不客气。另请参阅第二种方法,也许它会更适合。
    • 太棒了。方法 2 是我的首选方法。谢谢!
    • 嗨。第一种方法不再起作用,因为 BsonBaseSerializer 类不再是 sdk 的一部分。
    【解决方案2】:

    我尝试了上面的示例,但似乎某些类结构发生了变化。我有一个名为 BuildingNumber 的 JSON 字段,该字段大部分时间都有数字,但如果是 Flats 或 Cottages,则其留空。下面的代码按预期工作

    public class BsonStringNumericSerializer : SerializerBase<string>
    {
        public override string Deserialize(BsonDeserializationContext context, BsonDeserializationArgs args)
        {
            var bsonType = context.Reader.CurrentBsonType;
            switch (bsonType)
            {
                case BsonType.Null:
                    context.Reader.ReadNull();
                    return null;
                case BsonType.String:
                    return context.Reader.ReadString();
                case BsonType.Int32:
                    return context.Reader.ReadInt32().ToString(CultureInfo.InvariantCulture);
                default:
                    var message = string.Format($"Custom Cannot deserialize BsonString or BsonInt32 from BsonType {bsonType}");
                    throw new BsonSerializationException(message);
            }
        }
    
        public override void Serialize(BsonSerializationContext context, BsonSerializationArgs args, string value)
        {
            if (value != null)
            {
                if (int.TryParse(value, out var result))
                {
                    context.Writer.WriteInt32(result);
                }
                else
                {
                    context.Writer.WriteString(value);
                }
            }
            else
            {
                context.Writer.WriteNull();
            }
        }
    }
    
    [BsonElement("BUILDING_NUMBER")]
    [BsonSerializer(typeof(BsonStringNumericSerializer))]
    public string BuildingNumberString { get; set; }
    

    【讨论】:

      【解决方案3】:

      这适用于 C# Mongo 2.0+

      public class TestingObjectTypeSerializer : IBsonSerializer
      {
          public Type ValueType { get; } = typeof(string);
      
          public object Deserialize(BsonDeserializationContext context, BsonDeserializationArgs args)
          {
              if (context.Reader.CurrentBsonType == BsonType.Int32) return GetNumberValue(context);
      
              return context.Reader.ReadString();
          }
      
          public void Serialize(BsonSerializationContext context, BsonSerializationArgs args, object value)
          {
              context.Writer.WriteString(value as string);
          }
      
          private static object GetNumberValue(BsonDeserializationContext context)
          {
              var value = context.Reader.ReadInt32();
      
              switch (value)
              {
                  case 1:
                      return "one";
                  case 2:
                      return "two";
                  case 3:
                      return "three";
                  default:
                      return "BadType";
              }
          }
      }
      

      你可以像这样使用它

      public class TestingObject
      {
          public string FirstName { get; set; }
      
          public string LastName { get; set; }
      
          [BsonSerializer(typeof(TestingObjectTypeSerializer))]
          public string TestingObjectType { get; set; }
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2015-01-08
        • 2016-12-09
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多