使用mongodb BsonSerializer 序列化和反序列化数据

Use mongodb BsonSerializer to serialize and deserialize data

我有复杂的 class 像这样:

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

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

我的目标是将对象从计算机 A 发送到计算机 B

我能够通过使用 BinaryFormatter serialization 实现我的目标。它使我能够序列化像 Animal 这样的复杂 classes,以便将对象从计算机 A 传输到计算机 B。序列化速度非常快,我只需要担心在我的 class 之上放置一个可序列化的属性es。但现在 BinaryFormatter 已过时,如果您在互联网上阅读,dotnet 的未来版本可能会删除它。

因此我有以下选择:

  1. 使用System.Text.Json 这种方法不适用于多态性。换句话说,我无法反序列化一系列猫狗。所以我会尽量避免。

  2. 使用protobuf 我不想为每个 class 创建 protobuf 映射文件。我有 40 多个 classes 这是很多工作。或者也许有一个我不知道的转换器?但是转换器如何足够聪明,知道我的动物阵列可以有猫和狗?

  3. 使用Newtonsoft (json.net) 我可以使用此解决方案并构建如下内容: . Or even better serialize the objects with a type like this: 。所以这可能是我的选择。

  4. 使用MongoDB.Bson.Serialization.BsonSerializer 因为我要处理很多我们正在使用的复杂对象MongoDB。 MongoDB 能够轻松存储 Farm 对象。我的目标是以二进制格式从数据库中检索对象并将该二进制数据发送到另一台计算机并使用 BsonSerializer 将它们反序列化回对象。

  5. 让电脑B远程连接数据库。我无法使用此选项,因为我们的要求之一是通过 API 完成所有操作。出于安全原因,我们不允许远程连接到数据库。

我希望可以使用步骤 4。这将是最有效的,因为我们已经在使用 MongoDB。如果我们使用可行的第 3 步,我们将执行额外的步骤。我们不需要 json 格式的数据。为什么不以二进制形式发送它,并在计算机 B 收到后反序列化它? MongoDB.Driver 已经在这样做了。我希望我知道它是如何做到的。

这是我到目前为止所做的工作:

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