Home > Net >  Use mongodb BsonSerializer to serialize and deserialize data
Use mongodb BsonSerializer to serialize and deserialize data

Time:03-14

I have complex classes like this:

abstract class Animal { ... }
class Dog: Animal{ ... }
class Cat: Animal{ ... }

class Farm{
    public List<Animal> Animals {get;set;}
    ...
}

My goal is to send objects from computer A to computer B

I was able to achieve my goal by using BinaryFormatter serialization. It enabled me to serialize complex classes like Animal in order to transfer objects from computer A to computer B. Serialization was very fast and I only had to worry about placing a serializable attribute on top of my classes. But now BinaryFormatter is obsolete and if you read on the internet future versions of dotnet may remove that.

As a result I have these options:

  1. Use System.Text.Json This approach does not work well with polymorphism. In other words I cannot deserialize an array of cats and dogs. So I will try to avoid it.

  2. Use protobuf I do not want to create protobuf map files for every class. I have over 40 classes this is a lot of work. Or maybe there is a converter that I am not aware of? But still how will the converter be smart enough to know that my array of animals can have cats and dogs?

  3. Use Newtonsoft (json.net) I could use this solution and build something like this: https://stackoverflow.com/a/19308474/637142 . Or even better serialize the objects with a type like this: https://stackoverflow.com/a/71398251/637142. So this will probably be my to go option.

  4. Use MongoDB.Bson.Serialization.BsonSerializer Because I am dealing with a lot of complex objects we are using MongoDB. MongoDB is able to store a Farm object easily. My goal is to retrieve objects from the database in binary format and send that binary data to another computer and use BsonSerializer to deserialize them back to objects.

  5. Have computer B connect to the database remotely. I cannot use this option because one of our requirements is to do everything through an API. For security reasons we are not allowed to connect remotely to the database.

I am hopping I can use step 4. It will be the most efficient because we are already using MongoDB. If we use step 3 which will work we are doing extra steps. We do not need the data in json format. Why not just sent it in binary and deserialize it once it is received by computer B? MongoDB.Driver is already doing this. I wish I knew how it does it.

This is what I have worked so far:

    MongoClient m = new MongoClient("mongodb://localhost:27017");
    var db = m.GetDatabase("TestDatabase");

    var collection = db.GetCollection<BsonDocument>("Farms");

    // I have 1s and 0s in here. 
    var binaryData = collection.Find("{}").ToBson();

    // this is not readable
    var t = System.Text.Encoding.UTF8.GetString(binaryData);
    Console.WriteLine(t);

    // how can I convert those 0s and 1s to a Farm object?

CodePudding user response:

    var collection = db.GetCollection<RawBsonDocument>(nameof(this.Calls));

    var sw = new Stopwatch();
    var sb = new StringBuilder();

    sw.Start();

    // get items
    IEnumerable<RawBsonDocument>? objects = collection.Find("{}").ToList();

    sb.Append("TimeToObtainFromDb: ");
    sb.AppendLine(sw.Elapsed.TotalMilliseconds.ToString());
    sw.Restart();


    var ms = new MemoryStream();

    var largestSixe = 0;

    // write data to memory stream for demo purposes. on real example I will write this to a tcpSocket
    foreach (var item in objects)
    {
        var bsonType = item.BsonType;
        // write object
        var bytes = item.ToBson();

        ushort sizeOfBytes = (ushort)bytes.Length;

        if (bytes.Length > largestSixe)
            largestSixe = bytes.Length;

        var size = BitConverter.GetBytes(sizeOfBytes);

        ms.Write(size);

        ms.Write(bytes);
    }


    sb.Append("time to serialze into bson to memory: ");
    sb.AppendLine(sw.Elapsed.TotalMilliseconds.ToString());
    sw.Restart();


    // now on the client side on computer B lets pretend we are deserializing the stream
    ms.Position = 0;

    var clones = new List<Call>();

    byte[] sizeOfArray = new byte[2];
    byte[] buffer = new byte[102400]; // make this large because if an document is larger than 102400 bytes it will fail!
    while (true)
    {
        var i = ms.Read(sizeOfArray, 0, 2);
        if (i < 1)
            break;

        var sizeOfBuffer = BitConverter.ToUInt16(sizeOfArray);

        int position = 0;
        while (position < sizeOfBuffer)
            position = ms.Read(buffer, position, sizeOfBuffer - position);

        //using var test = new RawBsonDocument(buffer);
        using var test = new RawBsonDocumentWrapper(buffer , sizeOfBuffer);
        var identityBson = test.ToBsonDocument();



        var cc = BsonSerializer.Deserialize<Call>(identityBson);
        clones.Add(cc);
    }


    sb.Append("time to deserialize from memory into clones: ");
    sb.AppendLine(sw.Elapsed.TotalMilliseconds.ToString());
    sw.Restart();


    var serializedjs = new List<string>();
    foreach(var item in clones)
    {
        var foo = item.SerializeToJsStandards();
        if (foo.Contains("jaja"))
            throw new Exception();
        serializedjs.Add(foo);
    }

    sb.Append("time to serialze into js: ");
    sb.AppendLine(sw.Elapsed.TotalMilliseconds.ToString());
    sw.Restart();


    foreach(var item in serializedjs)
    {
        try
        {
            var obj = item.DeserializeUsingJsStandards<Call>();
            if (obj is null)
                throw new Exception();
            if (obj.IdAccount.Contains("jsfjklsdfl"))
                throw new Exception();
        }
        catch(Exception ex)
        {
            Console.WriteLine(ex);
            throw;
        }
        
    }

    sb.Append("time to deserialize js: ");
    sb.AppendLine(sw.Elapsed.TotalMilliseconds.ToString());
    sw.Restart();
  • Related