简介
其实已经有许多用来解析JSON格式数据的库了,为什么我们还想要再创造一个呢?
因为.NET 4.0框架引入了一个新的类型:dynamic!
背景
dynamic实际上是一个静态类型,但是编译器看待它与其它的类型不同。编译器遇到dynamic类型时不会作任何的类型安全检查(绕过了静态类型检查)
例如:
03 | static void Main( string [] args) |
07 | static void func(dynamic obj) |
上面这程序调用带有一个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(...)
以下是一个实现了所有上述方法的例子:
001 | public class DynamicConsoleWriter : DynamicObject |
003 | protected string first = "" ; |
004 | protected string last = "" ; |
012 | public override bool TryBinaryOperation(BinaryOperationBinder binder, |
013 | object arg, out object result) |
015 | bool success = false ; |
016 | if (binder.Operation == System.Linq.Expressions.ExpressionType.Add) |
018 | Console.WriteLine( "I have to think about that" ); |
024 | public override bool TryUnaryOperation(UnaryOperationBinder binder, out object result) |
026 | bool success = false ; |
027 | if (binder.Operation == System.Linq.Expressions.ExpressionType.Increment) |
029 | Console.WriteLine( "I will do it later" ); |
035 | public override bool TryGetIndex(GetIndexBinder binder, |
036 | object [] indexes, out object result) |
039 | if ( ( int )indexes[0] == 0) |
043 | else if (( int )indexes[0] == 1) |
049 | public override bool TrySetIndex(SetIndexBinder binder, object [] indexes, object value) |
051 | if (( int )indexes[0] == 0) |
053 | first = ( string )value; |
055 | else if (( int )indexes[0] == 1) |
057 | last = ( string )value; |
061 | public override bool TryGetMember(GetMemberBinder binder, out object result) |
063 | string name = binder.Name.ToLower(); |
064 | bool success = false ; |
071 | else if (name == "first" ) |
078 | public override bool TrySetMember(SetMemberBinder binder, object value) |
080 | string name = binder.Name.ToLower(); |
081 | bool success = false ; |
084 | last = ( string )value; |
087 | else if (name == "first" ) |
089 | first = ( string )value; |
094 | public override bool TryInvokeMember(InvokeMemberBinder binder, |
095 | object [] args, out object result) |
097 | string name = binder.Name.ToLower(); |
098 | bool success = false ; |
100 | if (name == "writelast" ) |
102 | Console.WriteLine(last); |
105 | else if (name == "writefirst" ) |
107 | Console.WriteLine(first); |
以下展示了我们如何使用它们:
01 | dynamic dynamicConsoleWriter = new DynamicConsoleWriter(); |
02 | dynamicConsoleWriter.First = "I am just a" ; |
03 | dynamicConsoleWriter.Last = " Lion!" ; |
05 | var result1 = dynamicConsoleWriter + 2; |
06 | var result2 = ++dynamicConsoleWriter; |
07 | dynamicConsoleWriter[0] = "Hello" ; |
08 | var result3 = dynamicConsoleWriter[0]; |
09 | var result4 = dynamicConsoleWriter.First; |
10 | var result5 = dynamicConsoleWriter.Last; |
11 | var result6 = dynamicConsoleWriter.Count; |
13 | dynamicConsoleWriter.WriteFirst(); |
14 | dynamicConsoleWriter.WriteLast(); |
另外一个动态类型很酷的特性就是他们实现了专一性,也就是说,大部分特定的函数调用在运行时将会被选择。
当遇到类型找不到时将会抛出RuntimeBinderException异常。该异常可通过实现一个接受object值的函数来避免。
01 | public class Specificity |
03 | public static void printDynamic(dynamic obj) |
07 | protected static void print(List< int > list) |
09 | foreach (var item in list) |
11 | Console.WriteLine(item); |
14 | protected static void print( object obj) |
16 | Console.WriteLine( "I do not know how to print you" ); |
当我们传递任何参数至printDynamic函数除了List<int>时,print(object obj)将会被调用。
动态JSON转换器
JavaScriptSerializer将会把一个JSON字符串转换至一个IDictionary<string,object>中。
JavaScriptSerializer在System.Web.Extensions中声明,使用System.Web.Script.Serialization来编译代码。
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对象的简单迭代。
以下便是如何使用动态解析器的例子:
03 | " \"firstName\": \"John\"," + |
04 | " \"lastName\" : \"Smith\"," + |
08 | " \"streetAddress\": \"21 2nd Street\"," + |
09 | " \"city\" : \"New York\"," + |
10 | " \"state\" : \"NY\"," + |
11 | " \"postalCode\" : \"11229\"" + |
16 | " \"type\" : \"home\"," + |
17 | " \"number\": \"212 555-1234\"" + |
20 | " \"type\" : \"fax\"," + |
21 | " \"number\": \"646 555-4567\"" + |
26 | var serializer = new JavaScriptSerializer(); |
27 | serializer.RegisterConverters( new [] { new DynamicJsonConverter() }); |
28 | dynamic data = serializer.Deserialize< object >(json); |
29 | Console.WriteLine(data.firstName); |
30 | Console.WriteLine(data.lastName); |
31 | Console.WriteLine(data.age); |
32 | Console.WriteLine(data.address.postalCode); |
33 | Console.WriteLine(data.phoneNumber.Count); |
34 | Console.WriteLine(data.phoneNumber[0].type); |
35 | Console.WriteLine(data.phoneNumber[1].type); |
36 | foreach (var pn in data.phoneNumber) |
38 | Console.WriteLine(pn.number); |
40 | Console.WriteLine(data.ToString()); |
43 | dynamic jdata = new DynamicJsonObject(); |
44 | dynamic item1 = new DynamicJsonObject(); |
45 | dynamic item2 = new DynamicJsonObject(); |
46 | ArrayList items = new ArrayList(); |
50 | item2.Price = 19000000.99; |
53 | jdata.Date = "06/06/2004" ; |
55 | Console.WriteLine(jdata.ToString()); |
鸣谢
初始化动态JSON转换器是由Shawn Weisfeld编写
License
此文章涉及到的任何源代码以及文件都在The Code Project Open License (CPOL)证书公开
via:codeproject.com OSChina原创编译