简介 其实已经有许多用来解析JSON格式数据的库了,为什么我们还想要再创造一个呢? 因为.NET 4.0框架引入了一个新的类型:dynamic! 背景 dynamic实际上是一个静态类型,但是编译器看待它与其它的类型不同。编译器遇到dynamic类型时不会作任何的类型安全检查(绕过了静态类型检查) 例如: view source print? 01 class Program 02 { 03 static void Main(string[] args) 04 { 05 func(1); // 'int' does not contain a definition for 'error' 06 } 07 static void func(dynamic obj) 08 { 09 obj.error = "oops"; 10 } 11 } 上面这程序调用带有一个int型变量的func函数,当然,int类型没有一个名为error属性,但是程序在编译时不会产生任何错误。而一切在运行时便看起来有所不同了。会抛出出错信息为'int'不包含'error'的定义的RuntimeBinderException异常。 DynamicObject 加入了dynamic功能的.NET层叫做Dynamic Language Runtime(DLR)。DLR处于Common Language Runtime(CLR)的顶部。动态对象都实现了IDynamicMetaObjectProvider接口。 DynamicObject是一个实现了IDynamicMetaObjectProvider的抽象类并提供一系列的基本操作。继承自DynamicObject的一个类可以重载例如TrySetMember以及TryGetMember方法来set和get属性。 以下是DynamicObject几个比较重要的可重载的成员函数,以实现对动态类型的自定义表现。 TryBinaryOperation - 二进制操作 *,+,-... TryUnaryOperation - 一元操作 --,++,... TryGetIndex - 以索引访问一个对象操作 [] TrySetIndex - 以索引设置一个对象操作 [] TryGetMember - 获取一个属性值,例如:obj.property_name TrySetMember - 设置一个属性值,例如:obj.property_name = "value" TryInvokeMember - 调用一个方法,例如:obj.SomeMethod(...) 以下是一个实现了所有上述方法的例子: view source print? 001 public class DynamicConsoleWriter : DynamicObject 002 { 003 protected string first = ""; 004 protected string last = ""; 005 public int Count 006 { 007 get 008 { 009 return 2; 010 } 011 } 012 public override bool TryBinaryOperation(BinaryOperationBinder binder, 013 object arg, out object result) 014 { 015 bool success = false; 016 if (binder.Operation == System.Linq.Expressions.ExpressionType.Add) 017 { 018 Console.WriteLine("I have to think about that"); 019 success = true; 020 } 021 result = this; 022 return success; 023 } 024 public override bool TryUnaryOperation(UnaryOperationBinder binder, out object result) 025 { 026 bool success = false; 027 if (binder.Operation == System.Linq.Expressions.ExpressionType.Increment) 028 { 029 Console.WriteLine("I will do it later"); 030 success = true; 031 } 032 result = this; 033 return success; 034 } 035 public override bool TryGetIndex(GetIndexBinder binder, 036 object[] indexes, out object result) 037 { 038 result = null; 039 if ( (int)indexes[0] == 0) 040 { 041 result = first; 042 } 043 else if ((int)indexes[0] == 1) 044 { 045 result = last; 046 } 047 return true; 048 } 049 public override bool TrySetIndex(SetIndexBinder binder, object[] indexes, object value) 050 { 051 if ((int)indexes[0] == 0) 052 { 053 first = (string)value; 054 } 055 else if ((int)indexes[0] == 1) 056 { 057 last = (string)value; 058 } 059 return true; 060 } 061 public override bool TryGetMember(GetMemberBinder binder, out object result) 062 { 063 string name = binder.Name.ToLower(); 064 bool success = false; 065 result = null; 066 if (name == "last") 067 { 068 result = last; 069 success = true; 070 } 071 else if (name == "first") 072 { 073 result = first; 074 success = true; 075 } 076 return success; 077 } 078 public override bool TrySetMember(SetMemberBinder binder, object value) 079 { 080 string name = binder.Name.ToLower(); 081 bool success = false; 082 if (name == "last") 083 { 084 last = (string)value; 085 success = true; 086 } 087 else if (name == "first") 088 { 089 first = (string)value; 090 success = true; 091 } 092 return success; 093 } 094 public override bool TryInvokeMember(InvokeMemberBinder binder, 095 object[] args, out object result) 096 { 097 string name = binder.Name.ToLower(); 098 bool success = false; 099 result = true; 100 if (name == "writelast") 101 { 102 Console.WriteLine(last); 103 success = true; 104 } 105 else if (name == "writefirst") 106 { 107 Console.WriteLine(first); 108 success = true; 109 } 110 return success; 111 } 112 } 以下展示了我们如何使用它们: view source print? 01 dynamic dynamicConsoleWriter = new DynamicConsoleWriter(); 02 dynamicConsoleWriter.First = "I am just a"; // TrySetMember is invoked 03 dynamicConsoleWriter.Last = " Lion!"; // TrySetMember is invoked 04 05 var result1 = dynamicConsoleWriter + 2; // TryBinaryOperation is invoked 06 var result2 = ++dynamicConsoleWriter; // TryUnaryOperation is invoked 07 dynamicConsoleWriter[0] = "Hello"; // TrySetIndex is invoked 08 var result3 = dynamicConsoleWriter[0]; // TryGetIndex is invoked 09 var result4 = dynamicConsoleWriter.First; // TryBinaryOperation is invoked 10 var result5 = dynamicConsoleWriter.Last; // TryBinaryOperation is invoked 11 var result6 = dynamicConsoleWriter.Count; // DynamicConsoleWriter Count property is called 12 13 dynamicConsoleWriter.WriteFirst(); // TryInvokeMember is invoked 14 dynamicConsoleWriter.WriteLast(); // TryInvokeMember is invoked 另外一个动态类型很酷的特性就是他们实现了专一性,也就是说,大部分特定的函数调用在运行时将会被选择。 当遇到类型找不到时将会抛出RuntimeBinderException异常。该异常可通过实现一个接受object值的函数来避免。 view source print? 01 public class Specificity 02 { 03 public static void printDynamic(dynamic obj) 04 { 05 print(obj); 06 } 07 protected static void print(List<int> list) 08 { 09 foreach (var item in list) 10 { 11 Console.WriteLine(item); 12 } 13 } 14 protected static void print(object obj) 15 { 16 Console.WriteLine("I do not know how to print you"); 17 } 18 } 当我们传递任何参数至printDynamic函数除了List<int>时,print(object obj)将会被调用。 动态JSON转换器 JavaScriptSerializer将会把一个JSON字符串转换至一个IDictionary<string,object>中。 JavaScriptSerializer在System.Web.Extensions中声明,使用System.Web.Script.Serialization来编译代码。 view source print? 1 var serializer = new JavaScriptSerializer(); 2 serializer.RegisterConverters(new[] { new DynamicJsonConverter() }); 3 dynamic data = serializer.Deserialize<object>(json); serializer。Deserialize<object>(json)转换JSON字符串并调用JavaScriptConverter的Deserialize方法,我们重载此方法来从Deserialize方法中提供的dictionary创建新的DynamicJsonObjec。 DynamicObject如魔法搬将一个dictionary转换为包含所有JSON属性的对象。 ExpandoObject是一个新类但却是不是我们需要的,它提供的无法满足我们更加灵活的需求。 每个序列化的dictionary中的值是一个简单类型(也就是int,string,double),IDictionary<string,object>({...})或者ArrayList。 我们重载了DynamicObject的TryGetMember函数来处理所有这三种类型的序列化dictionary值。 同样我们也会实现TrySetMember方法以添加新的域到JSON对象中,并且将实现IEnumerable接口来实现对动态JSON对象的简单迭代。 以下便是如何使用动态解析器的例子: view source print? 01 const string json = 02 "{" + 03 " \"firstName\": \"John\"," + 04 " \"lastName\" : \"Smith\"," + 05 " \"age\" : 25," + 06 " \"address\" :" + 07 " {" + 08 " \"streetAddress\": \"21 2nd Street\"," + 09 " \"city\" : \"New York\"," + 10 " \"state\" : \"NY\"," + 11 " \"postalCode\" : \"11229\"" + 12 " }," + 13 " \"phoneNumber\":" + 14 " [" + 15 " {" + 16 " \"type\" : \"home\"," + 17 " \"number\": \"212 555-1234\"" + 18 " }," + 19 " {" + 20 " \"type\" : \"fax\"," + 21 " \"number\": \"646 555-4567\"" + 22 " }" + 23 " ]" + 24 " }"; 25 26 var serializer = new JavaScriptSerializer(); 27 serializer.RegisterConverters(new[] { new DynamicJsonConverter() }); 28 dynamic data = serializer.Deserialize<object>(json); 29 Console.WriteLine(data.firstName); // John 30 Console.WriteLine(data.lastName); // Smith 31 Console.WriteLine(data.age); // 25 32 Console.WriteLine(data.address.postalCode); // 11229 33 Console.WriteLine(data.phoneNumber.Count); // 2 34 Console.WriteLine(data.phoneNumber[0].type); // home 35 Console.WriteLine(data.phoneNumber[1].type); // fax 36 foreach (var pn in data.phoneNumber) 37 { 38 Console.WriteLine(pn.number); // 212 555-1234, 646 555-4567 39 } 40 Console.WriteLine(data.ToString()); 41 42 // and creating JSON formatted data 43 dynamic jdata = new DynamicJsonObject(); 44 dynamic item1 = new DynamicJsonObject(); 45 dynamic item2 = new DynamicJsonObject(); 46 ArrayList items = new ArrayList(); 47 item1.Name = "Drone"; 48 item1.Price = 92000.3; 49 item2.Name = "Jet"; 50 item2.Price = 19000000.99; 51 items.Add(item1); 52 items.Add(item2); 53 jdata.Date = "06/06/2004"; 54 jdata.Items = items; 55 Console.WriteLine(jdata.ToString()); 鸣谢 初始化动态JSON转换器是由Shawn Weisfeld编写 License 此文章涉及到的任何源代码以及文件都在The Code Project Open License (CPOL)证书公开
浙公网安备 33010602011771号