简体   繁体   English

mongodb C#异常无法从BsonType Int32反序列化字符串

[英]mongodb C# exception Cannot deserialize string from BsonType Int32

I am new to using mongo db in C# , but I am trying to import large database in mongo db.我是在 C# 中使用 mongo db 的新手,但我正在尝试在 mongo db 中导入大型数据库。 MyDb consists entities having only simple parameters Id , Body , Title Tags. MyDb 由仅具有简单参数 Id 、 Body 、 Title Tags 的实体组成。

This is example of entity in mongo.这是 mongo 中的实体示例。

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

This is my class of mongoEntity in C#这是我在 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);
        }

    }

This is my code in Main method这是我在 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();
             }

When i run this code I am able to see some data, but I'v got exception "Cannot deserialize string from BsonType Int32."当我运行此代码时,我能够看到一些数据,但出现异常“无法从 BsonType Int32 反序列化字符串”。 I think that one of property is represented in database as int , but I dont know how to deal with it ?我认为其中一个属性在数据库中表示为 int ,但我不知道如何处理它? How come one property in one entity is int and the same property in another object is string ?为什么一个实体中的一个属性是 int 而另一个对象中的相同属性是 string ? What I have to do to read all database ?我必须做什么才能读取所有数据库?

Yes, String property in C# object has Int32 value in mongo storage, so you have exception during serialization (see code for MongoDB.Bson.Serialization.Serializers.BsonStringSerializer class). 是的,C#对象中的String属性在mongo存储中具有Int32值,因此在序列化期间您有异常(请参阅MongoDB.Bson.Serialization.Serializers.BsonStringSerializer类的代码)。

1) You can define your own serializer, which will deserialize Int32 values to string property as well as String ones. 1)您可以定义自己的序列化,这将反序列化Int32值字符串属性以及String的。 Here it is: 这里是:

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();
        }
    }
}

Then mark necessary properties (which have different types in MongoDB in your opinion) with this serializer, for example: 然后使用此序列化程序标记必要的属性(在您看来MongoDB中具有不同的类型),例如:

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

Also I've found very similar question here: Deserializing field when type is changed using MongoDb csharp driver 此外,我在这里发现了非常相似的问题: 使用MongoDb csharp驱动程序更改类型时反序列化字段


2) The second approach - is to 'normalize' your data in storage: convert all integer field values to string. 2)第二种方法 - 在存储中“标准化”您的数据:将所有整数字段值转换为字符串。 So, you should change field $type from 16 (32-bit integer) to 2 (string). 因此,您应该将字段$type从16(32位整数)更改为2(字符串)。 See BSON types . BSON类型 Let's do it for body field: 让我们为body领域做:

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

This will work in C# Mongo 2.0+ 这将适用于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";
        }
    }
}

And you can use it like 你可以像使用它一样

public class TestingObject
{
    public string FirstName { get; set; }

    public string LastName { get; set; }

    [BsonSerializer(typeof(TestingObjectTypeSerializer))]
    public string TestingObjectType { get; set; }
}

I tried the above example but looks like some class structures have changed. 我尝试了上面的例子,但看起来有些类结构已经改变了。 I have a JSON field called BuildingNumber which has number most of the time but in case of Flats or Cottages its left blank. 我有一个名为BuildingNumber的JSON字段,其中大部分时间都有数字,但在Flats或Cottages的情况下,它留空。 Code is below which is working as expected 代码低于其正常工作

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; }

Theoretically you can change strings to objects then you'll be able to deserialize int as well as string.从理论上讲,您可以将字符串更改为对象,然后您就可以反序列化 int 以及字符串。 So if you have所以如果你有

Tags as int in DB, this construction will deserialize it and then you can convert it as you wish.在 DB 中标记为 int,此构造将对其进行反序列化,然后您可以根据需要对其进行转换。

[BsonElement("Tags")]
public object Tags { get; set; }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM