Deserialize Json into Dictionary<string,object> using BsonSerializer causes FileFormatException

StackOverflow https://stackoverflow.com/questions/8279876

  •  09-03-2021
  •  | 
  •  

Question

I am using MongoDB to bring back a record and I have the Json correctly returned and I was planning on converting the Json into a Dictionary of the format Dictionary but whenever I run the following method it returns an empty Dictionary (Caused by an exception being caught).

 1.   public Dictionary<string, object> convertJsonToDict(string json)
 2.   { 
 3.       try
 4.       {
 5.           Dictionary<string, object> dict = new Dictionary<string,object>();
 6.           dict = (Dictionary<string, object>)BsonSerializer.Deserialize(BsonSerializer.Deserialize<QueryDocument>(json), typeof(Dictionary<string, object>));
 7.           return dict;
 8.       }
 9.       catch
10.        {
11.            return new  Dictionary<string, object>();
12.        }
13.    }

When debugging I found the exception to be on line 6 when it tries to Deserialize the Json and the error is

FileFormatException: A document being deserialized to System.Object must be empty.

The Json String being passed in is

  "{ 
  \"_id\" : ObjectId(\"4ed1062129d8145d74e9ab2f\"), 
  \"1\" : \"New Guy\", 
  \"2\" : \"26/11/2011\", 
  \"4_25\" : \"Yes\", 
  \"4_26\" : \"No\", 
  \"5_25\" : \"No\", 
  \"5_26\" : \"Yes\", 
  \"6_25\" : \"Yes\", 
  \"6_26\" : \"No\", 
  \"7_25\" : \"\", 
  \"7_26\" : \"Comment in second box\", 
  \"19_21\" : { \"verified\" : true }, 
  \"10_19\" : \"0%\", 
  \"10_20\" : \"75% +\", 
  \"11_19\" : \"Diffuse\", 
  \"11_20\" : \"Localised\", 
  \"12_19\" : \"0-25%\", 
  \"12_20\" : \"50-75%\", 
  \"13_19\" : \"25-50%\", 
  \"13_20\" : \"25-50%\", 
  \"14_19\" : \"50-75%\", 
  \"14_20\" : \"0-25%\", 
  \"15_19\" : \"75% +\", 
  \"15_20\" : \"0%\", 
  \"17\" : \"Some comments at the bottom\" 
  }"

What is going wrong? I suspect it is something to do with the fact that there is an object at key "19_21" but this shouldn't cause it to break, should it?

Note the line breaks are just to make the Json easier to read and are not passed in. Also I did not write this code so I don't know why BsonSerializer is being used here, of if there is a better option that I can use.

Edit: Thank you to the people who have responded, the double serialization was due to the value in *19_21* originally was stored as a Json string inside a document, it is now a document within a document so it's type was string before so this used to work, And correctly people have noted that I only need to deserialize once using

 dict = (Dictionary<string, object>)BsonSerializer.Deserialize(json, typeof(Dictionary<string, object>);

Thank you @L.B

This unfortunately still doesn't solve my problem though, it will work if the object is a string in *19_21* but will still get a File format error if I use the new format of Document within document, does anybody have any ideas of what I need to change to make this work correctly?

Was it helpful?

Solution

You are correct in your suspicion that it has to do with the embedded document. The problem is that even though your Dictionary is storing the values as type object, the deserializer has to pick some concrete subclass of object to actually instantiate, and when it gets to the { verified : true } embedded document it can't figure out what data type it should deserialize that to (if the embedded document were empty it would choose object, but it can't choose object when the document has values that need to be deserialized).

You could deserialize to a BsonDocument which is very similar to a Dictionary just that the values are of type BsonValue instead of object. In this case the embedded document would just be another instance of BsonDocument.

Another response also commented on the double calls to Deserialize. There should only be one call needed.

OTHER TIPS

Using Json.Net, you can parse your string as below

JObject jobj=  JObject.Parse(inputstr);

Console.WriteLine(jobj["_id"]);
Console.WriteLine(jobj["1"]);
Console.WriteLine(jobj["19_21"]["verified"]);
//OR
foreach (var kv in jobj)
{
    Console.WriteLine(kv.Key + ":" + kv.Value);
}

FYI, I also had a problem with this exception "FileFormatException: A document being deserialized to System.Object must be empty." when deserializing an object from the database and the object had a Dictionary field with some complex Json document inside.

It turns out that BSON serializer needs to know the actual type of the object to be deserialized, otherwise it will try to deserialize it as System.Object which fails. The actual type name should be passed in _t field of the json document:

{ _t = "System.Collections.Generic.Dictionary`2[System.String,System.Object]" ...}

Hope this will be helpful to someone.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top