简介

其实已经有许多用来解析JSON格式数据的库了,为什么我们还想要再创造一个呢?

因为.NET 4.0框架引入了一个新的类型:dynamic!

背景

dynamic实际上是一个静态类型,但是编译器看待它与其它的类型不同。编译器遇到dynamic类型时不会作任何的类型安全检查(绕过了静态类型检查)

例如:

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(...)

以下是一个实现了所有上述方法的例子:

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	}

以下展示了我们如何使用它们:

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值的函数来避免。

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来编译代码。

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对象的简单迭代。

以下便是如何使用动态解析器的例子:

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