A simple approach to hydrating the C# MongoDB Driver Objects | John V. Petersen


: 5

If you have taken the time to look at MongoDB, you have seen first hand, the data problem it solves. I’m not going to delve into the specifics of MongoDB. If you want a good place to grok the topic, check out 10gen’s MongoDB Site. In short order, you can easily get up and running with Mongo. What I want to focus on here is how to hydrate the BSONDocument object exposed in the MongoDB C# Driver.

Building up a BSONDocument through the invocation of its public methods is not a problem. For example, if we want create a BSONArray and then add it to a BSONDocument, we can easily do that:

BsonDocument Creation

  1. var bsonDocument = new BsonDocument();
  2. var bsonArray = new BsonArray().
  3.     Add(“some value”).
  4.     Add(“Another Value”).
  5.     Add(“Yet another value”);
  8. string key = “123”;
  10. bsonDocument.Add(key, bsonArray);

The bsonDocument Object has a handy ToJson method:

Objects have a ToJson() Method

  1. var jsonText = bsonDocument.ToJson();

Which yields:

{ “123” : [“some value”, “Another Value”, “Yet another value”] }

Wouldn’t be cool if we could do this:

Hydrating Bson from Json

  1. var bsonDocument = new BsonDocument().FromJson(“{ “123” : [“some value”, “Another Value”, “Yet another value”] }”);

Out of the box, the MongoDB C# Driver does not support this functionality. The good news is, it turned out to be fairly easy to get this functionality.

The first thing I needed was a Json Parser. There are many parsers out there. To give you an idea of the choice that exists, check out json.org. Toward the bottom of the page, there are many parsers listed. The parsers are categorized by their respective languages. The one that caught my eye for C# was the one that was titled: How do I write my own parser? I decided to use this parser to implement the solution. Essentially, this parser does two things. 1 – it validates the text to ensure it is well formed jSON. 2 – it creates a hashtable that is hydrated from the raw jSON. There can be three different types of objects in the hashtable: hashtable, arraylist and object. A hashtable is the equivalent of a document. Documents can be nested. Therefore, a hashtable can contain a hashtable. An array list, as the name implies, is an array. Finally, an object is just a key value pair. In the example above, the entire document is actually one key value pair. The key is “123” and the value is the array. A document can be as simple as:

Simple Key Value Pair

  1. var bsonDocument = new BsonDocument(new BsonElement(“key”, BsonValue.Create(“name”)));

Which evaluates to:

{ “key” : “name” }

If we want to use the jSON Parser to decode this simple document, we can run this code:

Hashtable from parsed jSON

  1. var hashTable = JsonParser.JSON.JsonDecode(“{ “key” : “name” }”) as Hashtable;

And we can then interrogate hashTable like this: hashTable[“key”];

This is all REAL simple stuff. What we tend to see in real life is much more complicated.  Here is a sample that I found that is quite nasty:

{ “glossary” :
{ “title” : “example glossary”,
“GlossDiv” :
{ “title” : “S”,
“GlossList” :
{ “GlossEntry” :
“ID” : “SGML”,
“SortAs” : “SGML”,
“GlossTerm” : “Standard Generalized Markup Language”,
“Acronym” : “SGML”,
“Abbrev” : “ISO 8879:1986″,
“GlossDef” :
{ “para” : “A meta-markup language, used to create markup languages such as DocBook.”,
“GlossSeeAlso” : [“GML”, “XML”],
“GlossSee” : “markup”

This is a jSON Document with a nested jSON Document, key value pairs and an array. What I want to be able to do is hydrate a bsonDocument with this text. We already have part of the solution than to the jSON Parser which creates a Hashtable. The next thing that needs to be done is to iterate over that Hashtable. To do that, we need our trusty friend recursion! It is in the next class that I consume the services of the jSON Parser to create the extension method:

FromJson Extension Method

  1.   public static class MongoDriverExtensions
  2.     {
  3.         public static BsonDocument FromJson(this BsonDocument document,string json)
  4.         {
  5.             var jsonObject = JsonParser.JSON.JsonDecode(json) as Hashtable;
  6.             enumerate(jsonObject, document, null);
  8.             return document;
  9.         }
  11.         static void enumerate(Object jsonObject, BsonDocument document, BsonArray array)
  12.         {
  13.             var hashtable = jsonObject as Hashtable;
  15.             if (hashtable != null)
  16.             {
  17.                 if (array != null)
  18.                 {
  19.                     var newDocument = new BsonDocument();
  20.                     array.Add(newDocument);
  21.                 }
  23.                 foreach (DictionaryEntry dictionaryEntry in hashtable)
  24.                 {
  26.                     if (dictionaryEntry.Value.GetType() == typeof(Hashtable))
  27.                     {
  28.                         var newDocument = new BsonDocument();
  29.                         if (array != null)
  30.                         {
  31.                             array.Add(newDocument);
  32.                             newDocument.Add(dictionaryEntry.Key.ToString(), BsonValue.Create(dictionaryEntry.Value));
  33.                         }
  34.                         else
  35.                         {
  36.                             document.Add(dictionaryEntry.Key.ToString(), newDocument);
  37.                         }
  39.                         enumerate(dictionaryEntry.Value, newDocument, array);
  40.                     }
  41.                     else
  42.                     {
  43.                         if (dictionaryEntry.Value.GetType() == typeof(ArrayList))
  44.                         {
  46.                             var newArray = new BsonArray();
  47.                             document.Add(dictionaryEntry.Key.ToString(), newArray);
  49.                             foreach (object entry in dictionaryEntry.Value as ArrayList)
  50.                             {
  51.                                 enumerate(entry, document, newArray);
  52.                             }
  53.                         }
  54.                         else
  55.                         {
  56.                             if (array != null)
  57.                             {
  58.                                 if (array.Count > 0 && array[array.Count – 1].GetType() == typeof(BsonDocument))
  59.                                 {
  60.                                     ((BsonDocument)array[array.Count – 1]).Add(dictionaryEntry.Key.ToString(), BsonValue.Create(dictionaryEntry.Value));
  61.                                 }
  62.                                 else
  63.                                 {
  64.                                     array.Add(BsonValue.Create(dictionaryEntry.Value));
  65.                                 }
  66.                             }
  67.                             else
  68.                             {
  69.                                 document.Add(dictionaryEntry.Key.ToString(), BsonValue.Create(dictionaryEntry.Value));
  70.                             }
  71.                         }
  72.                     }
  73.                 }
  74.             }
  75.             else
  76.             {
  77.                 if (array != null)
  78.                 {
  79.                     array.Add(BsonValue.Create(jsonObject));
  80.                 }
  81.             }
  82.         }
  85.     }
  87. }

The code is actually quite simple. Because of the nature of what a Hashtable is and the the fact it can contain any # of nested levels, we need to use recursion to fetch the values. Ultimately, I’m taking data from the hash and am invoking the MongoDB C# Driver’s BsonDocument methods to hydrate the BsonDocument.

Imagine that I have a text file that contains jSON that is also an embedded resource in a test project. We can do something like this:

FromJson in Action!

  1. string jsonText1 = new StreamReader(
  2.     Assembly.GetExecutingAssembly().GetManifestResourceStream(“MongoDBCSharpDriverTest.json.txt”)).
  3.     ReadToEnd();
  5. BsonDocument bsonDocument1 = new BsonDocument().FromJson(jsonText1);

A BsonDocument is now hydrated. This means that we can work with it directly and we can also use the object to persist to a Mongo DB instance.

From this point on, we are dealing with the MongoDB C# Driver BsonDocument class. This is still very much a work in progress. Nevertheless, I wanted to share it with the community as I’m sure several of my peers have been wondering how to quickly hydrate BsonDocuments!

The extension class is attached to this post.


< JVP >