簡體   English   中英

MongoDb 自定義集合序列化器

[英]MongoDb custom collection serializer

我有四個簡單的類

public class Zoo{
    public ObjectId Id { get; set; } 
    public List<Animal> Animals { get; set; }
}
public class Animal{
    public ObjectId Id { get; set; } 
    public string Name { get; set; } 
}
public class Tiger : Animal{
    public double Height { get; set; }
}
public class Zebra : Animal{
    public long StripesAmount { get; set; }
}

我創建了自定義序列化程序,它允許我將 Animal 對象存儲在不同的集合(“animals”)中。

class MyAnimalSerializer : SerializerBase<Animal>
{
    public override void Serialize(MongoDB.Bson.Serialization.BsonSerializationContext context, MongoDB.Bson.Serialization.BsonSerializationArgs args, Animal value)
    {
        context.Writer.WriteStartDocument();
        context.Writer.WriteName("_id");
        context.Writer.WriteObjectId(ObjectId.GenerateNewId());
        context.Writer.WriteName("_t");
        context.Writer.WriteString(value.GetType().Name);
        context.Writer.WriteName("Name");
        context.Writer.WriteString(value.Name);
        switch (value.AnimalType)
        {
            case AnimalType.Tiger:
                context.Writer.WriteName("Height");
                context.Writer.WriteDouble((value as Tiger).Height);
                break;
            case AnimalType.Zebra:
                context.Writer.WriteName("StripesAmount");
                context.Writer.WriteInt32((value as Zebra).StripesAmount);
                break;
            default:
                break;
        }
        context.Writer.WriteEndDocument();
    }

    public override Animal Deserialize(MongoDB.Bson.Serialization.BsonDeserializationContext context, MongoDB.Bson.Serialization.BsonDeserializationArgs args)
    {
        context.Reader.ReadStartDocument();

        ObjectId id = context.Reader.ReadObjectId();
        string object_type = context.Reader.ReadString();
        string animal_name = context.Reader.ReadString();
        switch (object_type) 
        {
            case "Tiger":
                double tiger_height = context.Reader.ReadDouble();
                context.Reader.ReadEndDocument();
                return new Tiger()
                {
                    Id = id,
                    Name = animal_name,
                    Height = tiger_height
                };
            default:
                long zebra_stripes = context.Reader.ReadInt64();
                context.Reader.ReadEndDocument();
                return new Zebra()
                {
                    Id = id,
                    Name = animal_name,
                    StripesAmount = zebra_stripes
                };
        }
        return null;
    }
}

效果很好,也允許我做這樣的事情:

MongoDB.Bson.Serialization.BsonSerializer.RegisterSerializer(typeof(Animal), new MyAnimalSerializer());
IMongoCollection<Animal> collection = db.GetCollection<Animal>("animals");
var lst = await collection.Find<Animal>(new BsonDocument()).ToListAsync();

但是當Animals 存儲在 Zoo 中並且無法從 Zoo 集合中反序列化 Zoo 時,我不能這樣做

IMongoCollection<Zoo> collection = db.GetCollection<Zoo>("zoocollection");
var lst = await collection.Find<Zoo>(new BsonDocument()).ToListAsync(); //not working here

是否可以為該字段創建自定義集合序列化程序?

public List<Animal> Animals { get; set; }

誰能舉個例子? 提前致謝。

非常感謝Anton Putau提供了最簡單的解決方案。

但還有另一個。 手動序列化對象:

public class MyListAnimalSerializer : SerializerBase<List<Animals>>
{
    public override void Serialize(MongoDB.Bson.Serialization.BsonSerializationContext context, MongoDB.Bson.Serialization.BsonSerializationArgs args, List<Animal> value)
    {
        context.Writer.WriteStartArray();
        foreach (Animal mvnt in value)
        {
            context.Writer.WriteStartDocument();
            switch (mvnt.GetType().Name)
            {
                case "Tiger":
                    //your serialization here
                    break;
                case "Zebra":
                    //your serialization here
                    break;
                default:
                    break;
            }
            context.Writer.WriteEndDocument();
        }
        context.Writer.WriteEndArray();
    }

    public override List<Animals> Deserialize(MongoDB.Bson.Serialization.BsonDeserializationContext context, MongoDB.Bson.Serialization.BsonDeserializationArgs args)
    {
        context.Reader.ReadStartArray();

        List<Animals> result = new List<Animals>();

        while (true)
        {
            try
            {
                //this catch block only need to identify the end of the Array
                context.Reader.ReadStartDocument();
            }
            catch (Exception exp)
            {
                context.Reader.ReadEndArray();
                break;
            }

            var type = context.Reader.ReadString();
            var _id = context.Reader.ReadObjectId();
            var name = context.Reader.ReadString();
            if (type == "Tiger")
            {
                double tiger_height = context.Reader.ReadDouble();
                result.Add(new Tiger()
                {
                    Id = id,
                    Name = animal_name,
                    Height = tiger_height
                });
            }
            else
            {
                long zebra_stripes = context.Reader.ReadInt64();
                result.Add(return new Zebra()
                {
                    Id = id,
                    Name = animal_name,
                    StripesAmount = zebra_stripes
                });
            }
            context.Reader.ReadEndDocument();
        }
        return result;
    }
}

並且您必須注釋 IEnumerable 字段以使用您的序列化程序:

[BsonSerializer(typeof(MyListAnimalSerializer))]
public List<Animal> Animals { get; set; }

您是否訪問過此文檔頁面? 還有多態類的例子。

這是我存儲對象的示例:

public class Zoo
{
    [BsonId]
    public ObjectId Id { get; set; }
    public List<Animal> Animals { get; set; }
}

[BsonDiscriminator(RootClass = true)]
[BsonKnownTypes(typeof(Tiger), typeof(Zebra))]
public class Animal
{
    [BsonId]
    public ObjectId Id { get; set; }
    public string Name { get; set; }
}

public class Tiger : Animal
{
    public double Height { get; set; }
}
public class Zebra : Animal
{
    public long StripesAmount { get; set; }
}

public class MongoDocumentsDatabase
{
    /// <summary>
    /// MongoDB Server
    /// </summary>
    private readonly MongoClient _client;

    /// <summary>
    /// Name of database 
    /// </summary>
    private readonly string _databaseName;

    public MongoUrl MongoUrl { get; private set; }

    /// <summary>
    /// Opens connection to MongoDB Server
    /// </summary>
    public MongoDocumentsDatabase(String connectionString)
    {
        MongoUrl = MongoUrl.Create(connectionString);
        _databaseName = MongoUrl.DatabaseName;
        _client = new MongoClient(connectionString);
    }

    /// <summary>
    /// Get database
    /// </summary>
    public IMongoDatabase Database
    {
        get { return _client.GetDatabase(_databaseName); }
    }
    public IMongoCollection<Zoo> Zoo { get { return Database.GetCollection<Zoo>("zoo"); } }
}
class Program
{
    static void Main(string[] args)
    {
        var connectionString =
            "mongodb://admin:admin@localhost:27017/testDatabase";
        var pr = new Program();

        pr.Save(connectionString);
        var zoo = pr.Get(connectionString);

        foreach (var animal in zoo.Animals)
        {
            Console.WriteLine(animal.Name + "  " + animal.GetType());
        }
    }


    public void Save(string connectionString)
    {
        var zoo = new Zoo
        {
            Animals = new List<Animal>
            {
                new Tiger
                {
                    Height = 1,
                    Name = "Tiger1"
                },
                new Zebra
                {
                    Name = "Zebra1",
                    StripesAmount = 100
                }
            }
        };

        var database = new MongoDocumentsDatabase(connectionString);
        database.Zoo.InsertOneAsync(zoo).Wait();
    }

    public Zoo Get(string connectionString)
    {
        var database = new MongoDocumentsDatabase(connectionString);
        var task = database.Zoo.Find(e => true).SingleAsync();
        task.Wait();

        return task.Result;
    }
}

以下是對象在數據庫中的存儲方式 (Robomongo)在此處輸入圖片說明

最終結果:在此處輸入圖片說明

試試這個反序列化的實現。 它將避免 try ... catch 實現。

public override List<Animals> Deserialize(MongoDB.Bson.Serialization.BsonDeserializationContext context, MongoDB.Bson.Serialization.BsonDeserializationArgs args)
{
    context.Reader.ReadStartArray();
    context.Reader.ReadBSonType();

    List<Animals> result = new List<Animals>();

    while (context.Reader.State != MongoDB.Bson.IO.BsonReaderState.EndOfArray)
    {
        context.Reader.ReadStartDocument();

        var type = context.Reader.ReadString();
        var _id = context.Reader.ReadObjectId();
        var name = context.Reader.ReadString();
        if (type == "Tiger")
        {
            double tiger_height = context.Reader.ReadDouble();
            result.Add(new Tiger()
            {
                Id = id,
                Name = animal_name,
                Height = tiger_height
            });
        }
        else
        {
            long zebra_stripes = context.Reader.ReadInt64();
            result.Add(return new Zebra()
            {
                Id = id,
                Name = animal_name,
                StripesAmount = zebra_stripes
            });
        }
        context.Reader.ReadEndDocument();
        context.Reader.ReadBsonType();
    }

    context.Reader.ReadEndArray();

    return result;
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM