Using C# 4.0 and dynamic to parse JSON

An alternative deserialisation approach is suggested here. I modified the code slightly to fix a bug and suit my coding style. All you need is this:

View Code
  1 using System;
  2 using System.Collections;
  3 using System.Collections.Generic;
  4 using System.Collections.ObjectModel;
  5 using System.Dynamic;
  6 using System.Linq;
  7 using System.Text;
  8 using System.Web.Script.Serialization;
  9 
 10 private sealed class DynamicJsonConverter : JavaScriptConverter
 11 {
 12     public override object Deserialize(IDictionary<stringobject> dictionary, Type type, JavaScriptSerializer serializer)
 13     {
 14         if (dictionary == null)
 15             throw new ArgumentNullException("dictionary");
 16 
 17         return type == typeof(object) ? new DynamicJsonObject(dictionary) : null;
 18     }
 19 
 20     public override IDictionary<stringobject> Serialize(object obj, JavaScriptSerializer serializer)
 21     {
 22         throw new NotImplementedException();
 23     }
 24 
 25     public override IEnumerable<Type> SupportedTypes
 26     {
 27         get { return new ReadOnlyCollection<Type>(new List<Type>(new[] { typeof(object) })); }
 28     }
 29 
 30     #region Nested type: DynamicJsonObject
 31 
 32     private sealed class DynamicJsonObject : DynamicObject
 33     {
 34         private readonly IDictionary<stringobject> _dictionary;
 35 
 36         public DynamicJsonObject(IDictionary<stringobject> dictionary)
 37         {
 38             if (dictionary == null)
 39                 throw new ArgumentNullException("dictionary");
 40             _dictionary = dictionary;
 41         }
 42 
 43         public override string ToString()
 44         {
 45             var sb = new StringBuilder("{");
 46             ToString(sb);
 47             return sb.ToString();
 48         }
 49 
 50         private void ToString(StringBuilder sb)
 51         {
 52             var firstInDictionary = true;
 53             foreach (var pair in _dictionary)
 54             {
 55                 if (!firstInDictionary)
 56                     sb.Append(",");
 57                 firstInDictionary = false;
 58                 var value = pair.Value;
 59                 var name = pair.Key;
 60                 if (value is string)
 61                 {
 62                     sb.AppendFormat("{0}:\"{1}\"", name, value);
 63                 }
 64                 else if (value is IDictionary<stringobject>)
 65                 {
 66                     new DynamicJsonObject((IDictionary<stringobject>)value).ToString(sb);
 67                 }
 68                 else if (value is ArrayList)
 69                 {
 70                     sb.Append(name + ":[");
 71                     var firstInArray = true;
 72                     foreach (var arrayValue in (ArrayList)value)
 73                     {
 74                         if (!firstInArray)
 75                             sb.Append(",");
 76                         firstInArray = false;
 77                         if (arrayValue is IDictionary<stringobject>)
 78                             new DynamicJsonObject((IDictionary<stringobject>)arrayValue).ToString(sb);
 79                         else if (arrayValue is string)
 80                             sb.AppendFormat("\"{0}\"", arrayValue);
 81                         else
 82                             sb.AppendFormat("{0}", arrayValue);
 83 
 84                     }
 85                     sb.Append("]");
 86                 }
 87                 else
 88                 {
 89                     sb.AppendFormat("{0}:{1}", name, value);
 90                 }
 91             }
 92             sb.Append("}");
 93         }
 94 
 95         public override bool TryGetMember(GetMemberBinder binder, out object result)
 96         {
 97             if (!_dictionary.TryGetValue(binder.Name, out result))
 98             {
 99                 // return null to avoid exception.  caller can check for null this way...
100                 result = null;
101                 return true;
102             }
103 
104             var dictionary = result as IDictionary<stringobject>;
105             if (dictionary != null)
106             {
107                 result = new DynamicJsonObject(dictionary);
108                 return true;
109             }
110 
111             var arrayList = result as ArrayList;
112             if (arrayList != null && arrayList.Count > 0)
113             {
114                 if (arrayList[0is IDictionary<stringobject>)
115                     result = new List<object>(arrayList.Cast<IDictionary<stringobject>>().Select(x => new DynamicJsonObject(x)));
116                 else
117                     result = new List<object>(arrayList.Cast<object>());
118             }
119 
120             return true;
121         }
122     }
123 
124     #endregion
125 }

You can use it like this:

View Code
1 string json = ...;
2 
3 var serializer = new JavaScriptSerializer();
4 serializer.RegisterConverters(new[] { new DynamicJsonConverter() });
5 
6 dynamic obj = serializer.Deserialize(json, typeof(object));

So, given a JSON string: 

 

View Code
1 {
2   "Items":[
3     { "Name":"Apple""Price":12.3 },
4     { "Name":"Grape""Price":3.21 }
5   ],
6   "Date":"21/11/2010"
7 }

The following code will work at runtime:

 

View Code
1 var data = serializer.Deserialize(json, typeof(object));
2 
3 data.Date; // "21/11/2010"
4 data.Items.Count; // 2
5 data.Items[0].Name; // "Apple"
6 data.Items[0].Price; // 12.3 (as a decimal)
7 data.Items[1].Name; // "Grape"
8 data.Items[1].Price; // 3.21 (as a decimal)

 

I'm interested in any discussion about this approach.

EDIT

I updated the code to fix a small bug (with lists of complex types) and to include a ToString method that outputs the JSON string, which I found useful for debugging. You can drop the two methods out if you don't want them as they aren't required for deserialisation.

It's pretty simple using Newtonsoft.Json:

View Code
1 var jsonSerializer = new JsonSerializer();
2 dynamic stuff = jsonSerializer.Deserialize(new JsonTextReader(new StringReader("{ 'Name': 'Jon Smith', 'Address': { 'City': 'New York', 'State': 'NY' }, 'Age': 42 }")));
3 
4 var name = stuff.Name;
5 var address = stuff.Address.City;

.Net 4.0 has a built-in library to do this:

View Code
1 using System.Web.Script.Serialization;
2 JavaScriptSerializer jss = new JavaScriptSerializer();
3 var d=jss.Deserialize<dynamic>(str);

This is the simplest way You can use it like this:

View Code
1  (new System.Collections.Generic.Mscorlib_DictionaryDebugView<string,object>(((System.Collections.Generic.Dictionary<string,object>)(obj[0])))).Items[1]

refer to:

http://stackoverflow.com/questions/3142495/deserialize-json-into-c-sharp-dynamic-object

这有一篇介绍动态解析原理的文章:http://www.codeproject.com/Articles/349646/Dynamic-JSON-parser

posted @ 2012-09-14 17:14  特务小强  阅读(7091)  评论(0编辑  收藏  举报