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:
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<string, object> 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<string, object> 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<string, object> _dictionary;
35
36 public DynamicJsonObject(IDictionary<string, object> 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<string, object>)
65 {
66 new DynamicJsonObject((IDictionary<string, object>)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<string, object>)
78 new DynamicJsonObject((IDictionary<string, object>)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<string, object>;
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[0] is IDictionary<string, object>)
115 result = new List<object>(arrayList.Cast<IDictionary<string, object>>().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:
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:
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:
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:
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:
2 JavaScriptSerializer jss = new JavaScriptSerializer();
3 var d=jss.Deserialize<dynamic>(str);
This is the simplest way You can use it like this:
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