Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Ignore a property when saving a POCO in MongoDB, but not ignoring it when serializing to JSON

I have the following model that I'm storing in MongoDB:

public class Person
{
    public ObjectId Id { get; set; }
    public Int PersonId { get; set; }
    public BsonDocument Resume { get; set; } // arbitrary JSON

    [BsonIgnore]
    public string FirstName { get; set; }    // stored elsewhere, 
                                             // populated at runtime
    [BsonIgnore]
    public string LastName { get; set; }     // ditto
}

Resume is a BsonDocument where I store arbitrary JSON that cannot be standardized into a POCO (each occurrence is vastly different).

I don't want to store the person's first and last names since that information is already stored in a SQL database and I don't want to have to worry about syncing changes. So I've decorated those parameters with [BsonIgnore]. When my app code retrieves the Person from MongoDB it populates the FirstName and LastName parameters before serializing it to JSON, like so:

public ActionResult GetPerson(int id)
{
    var query = New QueryDocument("_id", ObjectId.Parse(id));
    // personCollection is Collection<Person>
    var person = personCollection.FindOne(query);

    var pc = personCache.GetPerson(person.PersonId);

    person.FirstName = pc.FirstName;
    person.LastName = pc.LastName;

    var settings = New JsonWriterSettings() { Outputmode = JsonOutputMode.Strict }
    return Json(person.ToJson(settings), JsonRequestBehavior.AllowGet);
}

The resulting JSON, however, is missing FirstName and LastName nodes, apparently because they were decorated with [BsonIgnore].

Is there a way to tell the Official MongoDB C# driver to ignore saving the parameters in MongoDB but not ignore it when serializing into JSON?

like image 983
Chad Levy Avatar asked May 04 '14 06:05

Chad Levy


2 Answers

I don't think that's possible while you're using the driver itself to serialize your objects into json.

However you can (and probably should) use json.net to serialize into json. That way your BsonIgnore will have no effect on json serialization and the json attributes (like JsonIgnore) will have no effect on your mongo driver.

public class Person
{
    [JsonIgnore]
    publis string FullName {get; set;}
    [BsonIgnore]
    public string FirstName { get; set; }
    [BsonIgnore]
    public string LastName { get; set; }
}

In my opinion that will also be a better design because the bson is used for the DB and the json (mostly) is used for web consumption, and so be done using different tools.

like image 179
i3arnon Avatar answered Nov 18 '22 12:11

i3arnon


I decided to take I3arnon's advice and use Json.NET. The problem is Json.NET doesn't know how to handle most Mongo types, so serializing the Resume property was problematic because it's of type BsonDocument. Here's what I came up with:

Json.NET can normally serialize the result of a Mongo query, however the BsonDocument gave it a problem. In the case of my example, Json.NET could not serialize Person without special instructions on how to handle a BsonDocument.

First, I created a JsonConverter called BsonDocumentConverter:

public class BsonDocumentConverter : JsonConverter
{
    public override void WriteJson(Newtonsoft.Json.JsonWriter writer, object value, JsonSerializer serializer)
    {
        var settings = new JsonWriterSettings()
        {
            OutputMode = JsonOutputMode.Strict
        };

        writer.WriteRawValue(value.ToJson(settings));
    }

    public override object ReadJson(Newtonsoft.Json.JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        throw new NotImplementedException("Why would I want to deserialize?");
    }

    public override bool CanConvert(Type objectType)
    {
        return objectType == typeof(BsonDocument);
    }
}

This converter uses the MongoDB C# driver to serialize any BsonDocuments.

To use the converter:

var result = JsonConvert.SerializeObject(person, Formatting.None, 
                 new JsonSerializerSettings() { 
                     NullValueHandling = NullValueHandling.Ignore,
                     Converters = new List<JsonConverter>() { 
                         new BsonDocumentConverter() 
                         } 
                 });

return Json(result, JsonRequestBehavior.AllowGet);
like image 33
Chad Levy Avatar answered Nov 18 '22 10:11

Chad Levy