Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

mongodb C# exception Cannot deserialize string from BsonType Int32

Tags:

c#

mongodb

I am new to using mongo db in C# , but I am trying to import large database in mongo db. MyDb consists entities having only simple parameters Id , Body , Title Tags.

This is example of entity in mongo.

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

This is my class of mongoEntity in C#

 [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

  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." I think that one of property is represented in database as int , but I dont know how to deal with it ? How come one property in one entity is int and the same property in another object is string ? What I have to do to read all database ?

like image 758
Konrad Avatar asked Oct 29 '13 16:10

Konrad


People also ask

Does MongoDB use C++?

Welcome to the documentation site for the official MongoDB C++ driver. You can add the driver to your application to work with MongoDB using the C++11 or later standard.

Can you use MongoDB with C#?

By developing with C# and MongoDB together one opens up a world of possibilities. Console, window, and web applications are all possible. As are cross-platform mobile applications using the Xamarin framework.

What is a MongoDB driver?

The official MongoDB Node. js driver allows Node. js applications to connect to MongoDB and work with data. The driver features an asynchronous API which allows you to interact with MongoDB using Promises or via traditional callbacks.


2 Answers

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. 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; }
like image 176
cs981khx Avatar answered Sep 21 '22 07:09

cs981khx


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).

1) You can define your own serializer, which will deserialize Int32 values to string property as well as String ones. 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:

[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


2) The second approach - is to 'normalize' your data in storage: convert all integer field values to string. So, you should change field $type from 16 (32-bit integer) to 2 (string). See BSON types. Let's do it for body field:

db.train.find({ 'body' : { $type : 16 } }).forEach(function (element) {   
  element.body = "" + element.body;  // Convert field to string
  db.train.save(element);
});
like image 39
Shad Avatar answered Sep 21 '22 07:09

Shad