I am testing a number of scenarios with MongoDb to see how to recover from possible data issues.
I have classes (Addresses with collection of Address) with a zipcode pro
There are a couple of things going on. The main one is that you have to consume the input regardless of type or the deserialization process gets out of sync. I've tested your scenario writing a custom serializer called ZipCodeSerializer which handles nulls and writes ZipCodes as strings, but accepts either string or ints on input and converts the ints to string.
I used this class to test:
public class Address
{
public ObjectId Id;
public string ZipCode;
}
And this is the custom serializer I wrote:
public class ZipCodeSerializer : BsonBaseSerializer
{
public override object Deserialize(BsonReader bsonReader, Type nominalType, Type actualType, IBsonSerializationOptions options)
{
var bsonType = bsonReader.CurrentBsonType;
switch (bsonType)
{
case BsonType.Null:
bsonReader.ReadNull();
return null;
case BsonType.String:
return bsonReader.ReadString();
case BsonType.Int32:
return bsonReader.ReadInt32().ToString();
default:
var message = string.Format("ZipCodeSerializer expects to find a String or an Int32, not a {0}.", bsonType);
throw new BsonSerializationException(message);
}
}
public override void Serialize(BsonWriter bsonWriter, Type nominalType, object value, IBsonSerializationOptions options)
{
if (value == null)
{
bsonWriter.WriteNull();
}
else
{
bsonWriter.WriteString((string)value);
}
}
}
You have to make sure the custom serializer is hooked up, which you can do like this:
BsonClassMap.RegisterClassMap<Address>(cm =>
{
cm.AutoMap();
cm.GetMemberMap(a => a.ZipCode).SetSerializer(new ZipCodeSerializer());
});
So now the ZipCode field of the Address class will be handled by the custom serializer.
I created some test data using BsonDocument to make it easy to force particular stored versions of the data in my test collection:
collection.Drop();
collection.Insert(new BsonDocument());
collection.Insert(new BsonDocument("ZipCode", BsonNull.Value));
collection.Insert(new BsonDocument("ZipCode", "12345"));
collection.Insert(new BsonDocument("ZipCode", 56789));
Here's what the documents looked like using the mongo shell:
> db.test.find()
{ "_id" : ObjectId("4f871374e447ad238040e346") }
{ "_id" : ObjectId("4f871374e447ad238040e347"), "ZipCode" : null }
{ "_id" : ObjectId("4f871374e447ad238040e348"), "ZipCode" : "12345" }
{ "_id" : ObjectId("4f871374e447ad238040e349"), "ZipCode" : 56789 }
>
so we see that some ZipCodes are strings and some are ints (there's also a null thrown in).
And this is my test code:
foreach (var document in collection.FindAll())
{
Console.WriteLine(document.ToJson());
}
And the output of running the test code is:
{ "_id" : ObjectId("4f871374e447ad238040e346"), "ZipCode" : null }
{ "_id" : ObjectId("4f871374e447ad238040e347"), "ZipCode" : null }
{ "_id" : ObjectId("4f871374e447ad238040e348"), "ZipCode" : "12345" }
{ "_id" : ObjectId("4f871374e447ad238040e349"), "ZipCode" : "56789" }
Press Enter to continue
Notice that the zipcode that was an int in the database is now a string.
The full source code of my test program is available at:
http://www.pastie.org/3775465