2

Let us suppose we have a document to store our client which has fixed and extra fields. So here goes our sample class for the client:

public class Client
{
     public string Name{ get; set; }
     public string Address{ get; set; }
     public List<ExtraField> ExtraFields{ get; set; } //these fields are extra ones
}

In extra field class we have something like this:

public class ExtraField
{
    public string Key{ get; set; }
    public string Type { get; set; }
    public string Value { get; set; }
}

If I use standard driver's behaviour for serialization I would get smth like this:

{{Name:VName, Address:VAddress,  ExtraFields:[{Key:VKey,Type:VType,
Value:VValue},...]}, document2,...,documentn}

While I would like to have something like this:

{{Name:VName, Address:VAddress, VKey:VValue,...}, document2,...,documentn}

This would improve the search performance and is generally the point of document orientation.

How can I customize the serialization to such a way?

i3arnon
  • 113,022
  • 33
  • 324
  • 344
Yurii Hohan
  • 4,021
  • 4
  • 40
  • 54

2 Answers2

1

Here is the way I solved it (it works fine) and solved the issue.

using System; 
using System.Collections.Generic; 
using System.Linq; using System.Text; 
using MongoDB.Bson;
using MongoDB.Bson.Serialization; 
using MongoDB.Bson.Serialization.Serializers;

namespace TestDataGeneration {
    public class FieldsWrapper : IBsonSerializable
    {
        public List<DataFieldValue> DataFieldValues { get; set; }


        public object Deserialize(MongoDB.Bson.IO.BsonReader bsonReader, Type nominalType, IBsonSerializationOptions options)
        {
        if (nominalType != typeof(FieldsWrapper)) throw new ArgumentException("Cannot deserialize anything but self");
        var doc = BsonDocument.ReadFrom(bsonReader);
        var list = new List<DataFieldValue>();
        foreach (var name in doc.Names)
        {
            var val = doc[name];
            if (val.IsString)
                list.Add(new DataFieldValue {LocalIdentifier = name, Values = new List<string> {val.AsString}});
            else if (val.IsBsonArray)
            {
                DataFieldValue df = new DataFieldValue {LocalIdentifier = name};
                foreach (var elem in val.AsBsonArray)
                {
                    df.Values.Add(elem.AsString);
                }
                list.Add(df);
            }
        }
        return new FieldsWrapper {DataFieldValues = list};
        }


        public void Serialize(MongoDB.Bson.IO.BsonWriter bsonWriter, Type nominalType, IBsonSerializationOptions options)
        {
            if (nominalType != typeof (FieldsWrapper))
                throw new ArgumentException("Cannot serialize anything but self");
            bsonWriter.WriteStartDocument();
            foreach (var dataFieldValue in DataFieldValues)
            {

                bsonWriter.WriteName(dataFieldValue.LocalIdentifier);
                if (dataFieldValue.Values.Count != 1)
                {
                    var list = new string[dataFieldValue.Values.Count];
                    for (int i = 0; i < dataFieldValue.Values.Count; i++)
                        list[i] = dataFieldValue.Values[i];
                    BsonSerializer.Serialize(bsonWriter, list); 
                }
                else
                {
                    BsonSerializer.Serialize(bsonWriter, dataFieldValue.Values[0]); 
                }
            }
            bsonWriter.WriteEndDocument();
        }

    } }
Yurii Hohan
  • 4,021
  • 4
  • 40
  • 54
0

Essentially you just need to implement two methods yourself. First one to serialize an object as you want and second to deserialize an object from db to your Client class back:

1 Seialize client class:

public static BsonValue ToBson(Client client)
{
  if (client == null)
    return null;

  var doc = new BsonDocument();
  doc["Name"] = client.Name;
  doc["Address"] = client.Address;
  foreach (var f in client.ExtraFields)
  {
    var fieldValue = new BsonDocument();
    fieldValue["Type"] = f.Type;
    fieldValue["Value"] = f.Value;
    doc[f.Key] = fieldValue;
  }

  return doc;
}

2 Deserialize client object:

public static Client FromBson(BsonValue bson)
{
  if (bson == null || !bson.IsBsonDocument)
    return null;

  var doc = bson.AsBsonDocument;

  var client = new Client
  {
    ExtraFields = new List<ExtraField>(),
    Address = doc["Address"].AsString,
    Name = doc["Name"].AsString
  };
  foreach (var name in doc.Names)
  {
    var val = doc[name];
    if (val is BsonDocument)
    {
      var fieldDoc = val as BsonDocument;
      var field = new ExtraField
      {
        Key = name,
        Value = fieldDoc["Value"].AsString,
        Type = fieldDoc["Type"].AsString
      };
       client.ExtraFields.Add(field);
     }
   }

 return client;
}

3 Complete test example:

I've added above two method to your client class.

var server = MongoServer.Create("mongodb://localhost:27020");
var database = server.GetDatabase("SO");

var clients = database.GetCollection<Type>("clients");


var client = new Client() {Id = ObjectId.GenerateNewId().ToString()};
client.Name = "Andrew";
client.Address = "Address";
client.ExtraFields = new List<ExtraField>();
client.ExtraFields.Add(new ExtraField()
{
  Key = "key1",
  Type = "type1",
  Value = "value1"
});
client.ExtraFields.Add(new ExtraField()
{
  Key = "key2",
  Type = "type2",
  Value = "value2"
});

 //When inseting/saving use ToBson to serialize client
clients.Insert(Client.ToBson(client));

//When reading back from the database use FromBson method:
var fromDb = Client.FromBson(clients.FindOneAs<BsonDocument>());

4 Data structure in a database:

{
  "_id" : ObjectId("4e3a66679c66673e9c1da660"),
  "Name" : "Andrew",
  "Address" : "Address",
  "key1" : {
    "Type" : "type1",
    "Value" : "value1"
  },
  "key2" : {
    "Type" : "type2",
    "Value" : "value2"
  }
}

BTW: Take a look into serialization tutorial as well.

Andrew Orsich
  • 52,935
  • 16
  • 139
  • 134
  • As it is done in your sample, this serialization/deserialization methods are static and placed in some static class I suppose. In the tutorial they talk about IBsonSerializable interface and it will change the default serialization of your object. – Yurii Hohan Aug 04 '11 at 11:05
  • Also I've spoken about slighlty different JSON format. I think I would have to wrap my list in a separate class and implement IBsonSerializable in it, this would probably sove the problem – Yurii Hohan Aug 04 '11 at 11:07
  • 1
    @Hohhi: Essentially i've gave you an idea (and link to the documentation). How you will implement this - decide yourself. – Andrew Orsich Aug 04 '11 at 23:05