MongoDb自定义集合序列化程序

时间:2015-05-24 08:13:10

标签: c# mongodb serialization

我有四个简单的课程

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对象存储在一个不同的集合中(&#34; animals&#34;)。

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

但是当动物存储在动物园中时,我无法做同样的     并且无法从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; }

有人能举个例子吗? 提前致谢。

3 个答案:

答案 0 :(得分:5)

您是否访问过此document页面?还有多态类的例子。

这是我存储对象的例子:

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) enter image description here

最终结果: enter image description here

答案 1 :(得分:4)

非常感谢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; }

答案 2 :(得分:2)

尝试反序列化的此实现。它将避免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;
}