Click here to Skip to main content
15,887,453 members
Articles / Web Development / ASP.NET
Tip/Trick

Deeply Nested JSON Deserialization Using Recursion and JavaScriptSerializer

Rate me:
Please Sign up or sign in to vote.
5.00/5 (6 votes)
28 Apr 2015CPOL2 min read 59.5K   9   3
JSON Deserialization in depth concept and Dictionary<T,T> mapping. Special Thanks to Dr. Coral Walker

Introduction

Microsoft offers JavascriptSerializer as part of their support for JSON serialization which is a high level serializer that implements IDictionary<T,T>. There are so many developers who don’t use JavascriptSerializer for JSON Deserialization due to the fact that it is very difficult to handle the output. Architecturally, in deserialization, the returned value is abstracted as Dictionary<T,T>. Developers can deal with the result if the JSON object is not deeply nested, but when the JSON object is deeply nested, then developers try to spin out or find other serialization/deserialization processors for simplicity.

Deeply Nested JSON Problem in C#

For illustration, the following simple JSON Object can be turned in Dictionary in a straight-forward manner:

{
  "Key1" : "Value1" ,
  "Key2" : "Value2"
}

The deserialized abstraction would look like this:

key      value

key1     value1

key2     value2

Now let’s consider an extended scenario where the JSON Object is very nested such as the following object:

{
   {
     "Key1" :
             {
                "Sub-Key1" : "Sub-Value1" ,
                "Sub-Key2" : "Sub-Value2" ,
                 "Sub-Key3" : "Sub-Value3" ,
             }
    "Key2" :
             {
                 "Sub-Key1" :[ { "Sub-Sub-Key1" : "Val1" } , { "Sub-Sub-Key2" : "Val2" }  ] ,
                "Sub-Key2" :[ { "Sub-Sub-Key1" : "Val1" } , { "Sub-Sub-Key2" : "Val2" }  ]
             }
}

The internal interpretation of any JSON Serialization Processor for this particular object would have to be a dictionary that contains regular [Key,Value] Pairs, But in certain buckets, the value in each [Key,Value] is either a sub-dictionary or a collection of sub-dictionaries. From the view of point of any JSON Serialization Processor is as follows:

Key          Value

Key1        Dictionary

               Key                Value

               Sub-Key1       SubValue1

               Sub-Key2       SubValue2

               Sub-Key3       SubValue3

Key2       Dictionary

              Key                Value

              Sub-Key1       Collection

                                     3 Dictionaries

              Sub-Key2       Collection

                                     3 Dictionaries

Many solutions have been proposed to solve this problem with nested JSON, the most famous one suggests the use of the new feature in C# 4.0 that is ExpandoObject with Extension Method to the returned Base Dictionary, From my personal benchmarking, I found that generating ~100K ExpandoObjects requires between 50-100 MB of RAM for each request. Therefore, I propose an alternative for the use of ExpandoObject to hold inner dictionaries by making use of Method Recursion.

Using the Code

The first method is responsible for passing JSON Object as string and returns fully deserialized object in dictionary. Please note that this is an extension method to the datatype String. The second method is responsible for resolving the entire Object in the same hierarchy that is supposed to be represented as:

C#
public static Dictionary<string, object> deserializeJson(this string json)
    {
        JavaScriptSerializer serializer = new JavaScriptSerializer();
        Dictionary<string, object> dictionary = 
        	serializer.Deserialize<Dictionary<string, object>>(json);
        return dictionary;
    }
    
    static void resolveEntry(Dictionary<string, object> dic, string SupKey)
    {
        // Each entry in the main dictionary is [Table-Key , Table-Value]
        foreach (KeyValuePair<string, object> entry in dic)
        {
            if (entry.Value is Dictionary<string, object>)  // for Meta and Data
                resolveEntry((Dictionary<string, object>)entry.Value, entry.Key);
            else
                if (entry.Value is ICollection)
                
                // Checks whether the current Table-Value in Table is Sub-Dictionary|Collection|Flat Object
                {
                    foreach (var item in (ICollection)entry.Value) 
                    
                    // If the table base value is a collection of items then loop through them
                    {
                        if (item is Dictionary<string, object>)
                        
                            // If the Collection-Item is a Dictionary then submit it for resolving
                            resolveEntry((Dictionary<string, object>)item, SupKey + " : " + entry.Key);
                        else
                            Console.WriteLine(item.ToString());
                            
                            // If the Collection-Item is a Flat Object then output
                    }
                }
                else
                    Console.WriteLine(SupKey + " : " + 
                    	entry.Key.ToString() + "--->" + entry.Value.ToString());
                    
                    // The Current Table-Value is Flat Object
        }
    }
    
    // Caller :
    
 Dictionary<string, object> dic = plsfobject.deserializeJson();
 resolveEntry(dic, "Base");
 Console.ReadKey();

Points of Interest

The result output we print as with the following picture. However, you can extend the idea for passing each flat object to a database, static file, ... etc. also you can benchmark the code and compare it with other approaches and let me know.

Image 1

Recursion VS ExpandoObject

The following solution is supposed to achieve the same functionality but with bad performance in terms of speed and space. Please notice that both of these methods are extension methods and must live in static class. Also you can call them from the caller method as an extenstion to DataType String. The main difference is that in the first approach, you will need to initialize one ExpandoObject for holding the entire dictionary, whereas in the second approach the code produces linear Expando initializations with few level depth of JSON Nesting, and it can produce Exponential Expando initializations with very deep nested JSON objects.

C#
    public static ExpandoObject ToExpando(this string json)
    {
        JavaScriptSerializer serializer = new JavaScriptSerializer();
        IDictionary<string, object> dictionary = 
        	serializer.Deserialize<IDictionary<string, object>>(json);
        return dictionary.Expando();
    }

    public static ExpandoObject Expando(this IDictionary<string, object> dictionary)
    {
        ExpandoObject expandoObject = new ExpandoObject();
        IDictionary<string, object> objects = expandoObject;

        foreach (var item in dictionary)
        {
            bool processed = false;

            if (item.Value is IDictionary<string, object>)
            {
                objects.Add(item.Key, Expando((IDictionary<string, object>)item.Value));
                processed = true;
            }
            else if (item.Value is ICollection)
            {
                List<object> itemList = new List<object>();

                foreach (var item2 in (ICollection)item.Value)

                    if (item2 is IDictionary<string, object>)
                        itemList.Add(Expando((IDictionary<string, object>)item2));
                    else
                        itemList.Add(Expando(new Dictionary<string, 
                        	object> { { "Unknown", item2 } }));

                if (itemList.Count > 0)
                {
                    objects.Add(item.Key, itemList);
                    processed = true;
                }
            }

            if (!processed)
                objects.Add(item);
        }

        return expandoObject;
    }

// Caller :

string myJson = "Some Object String";

ExpandoObject myExObj = myJson.Expando();

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Web Developer university of babylon
Iraq Iraq
Ph.D Student at Cardiff University and Staff Member at University of Babylon College of Information Technology

Comments and Discussions

 
QuestionHow do i return value from the Function? Pin
Member 1492314623-Jun-21 2:23
Member 1492314623-Jun-21 2:23 
AnswerRe: How do i return value from the Function? Pin
Member 155532622-Mar-22 10:33
Member 155532622-Mar-22 10:33 
QuestionPlease can you share me full code of the JSON Deserialization Using Recursion and JavaScriptSerializer Pin
Member 145980651-Oct-19 7:36
Member 145980651-Oct-19 7:36 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.