因為個人在業余有嘗試在做一個游戲項目,所有的功能都是使用ajax的,因此要使用JSON作為媒介,然而如果是使用集成的類庫進行JSON的轉化,帶來的影響就是一個類在傳輸到頁面上的時候,其實僅僅只是需要其中的幾個屬性而已,如果嵌套的層數比較多,例如:一個類中包含其他的類或泛型或數組,這樣子,數據加起來以后多出了不少。也許有人會創建一些額外的類去來處理,那的確是可以解決這種問題,但是不同的功能所使用到的數據要是個有差別的話,那么增加類則就變成了一個無底的深淵了。
很早就有這個想法要把文章寫出來,可能是自己比較懶吧,總是因為公司的工作、業余游戲的開發或是其他問題沒能完成這個事情,今天終於下定決心把這份小小的心得寫出來。
首先,我們從單一的對象說起,一個對象內有一些屬性,首先設想這些屬性都是C#內簡單的類型,例如:Int16、Int32、Int64、bool、Guid、string、char、DateTime這些(如果缺少可根據轉換后的內容自行分組),我是將Int16、Int32、Int64、bool和Guid、string、char、DateTime分成2個不同的類型組別,因為在轉化的過程中,前一個分組不需要引號。
代碼如下:

1 /// <summary>
2 /// 基礎普通類型
3 /// </summary>
4 private static readonly Type[] BASE_NORMAL_TYPE = new Type[] { typeof(Int16), typeof(Int32), typeof(Int64), typeof(bool) };
5
6 /// <summary>
7 /// 基礎字符串類型
8 /// </summary>
9 private static readonly Type[] BASE_STRING_TYPE = new Type[] { typeof(Guid), typeof(string), typeof(char), typeof(DateTime) };
接着我們先來實現將一個只包含簡單類型的對象轉換成JSON,首先獲取類的屬性(要注意區分出那些非公開的靜態的、不包含get的屬性,雖然有一些默認選項),然后遍歷屬性,並一個個的轉換成對應的JSON,最后拼接在一起。
代碼如下:

1 var jsonList = new List<string>();
2 var type = obj.GetType();
3 foreach (var property in type.GetProperties(BindingFlags.GetProperty | BindingFlags.Instance | BindingFlags.Public))
4 {
5 var format = string.Empty;
6 if (Array.Exists(BASE_NORMAL_TYPE, t => t == type))
7 {
8 format = "{1}";
9 }
10 else if (Array.Exists(BASE_STRING_TYPE, t => t == type))
11 {
12 format = "\"{1}\"";
13 }
14 format = string.Concat("\"{0}\":", format);
15 var json = string.Format(format, property.Name, property.GetValue(obj, null));
16 jsonList.Add(json);
17 }
18 return string.Concat("{", string.Join(",", jsonList.ToArray()), "}");
簡單的完成以上的功能之后,這時我們需要加入數組的轉換,要判斷一個對象是否是一個數組,我們只需要獲取它的Type然后獲取屬性IsArray,如果為True則說明這是一個數組,因為數組是繼承自Array,我們可以現將對象轉化為Array,然后用我們前面寫好的轉化簡單類的方法,去一個個的轉化遍歷的對象。泛型類型最常用的就是列表IList<T>和字典IDictionary<TKey, TValue>,判斷一個對象是列表或者字典,我們可以通過對象的Type調用方法GetGenericArguments,該方法返回一個Int32類型,具體可以查看微軟提供的文檔,列表為1,而字典則為2,因為泛型列表繼承自IList,而字典繼承自IDictionary,於是我們仿照數組遍歷的方法去做轉換JSON。
於是我們要稍微調整剛才寫的ToJSON的方法,代碼如下:

1 var json = string.Empty;
2 if (Array.Exists(BASE_NORMAL_TYPE, t => t == type))
3 {
4 json = string.Format("{0}", obj);
5 }
6 else if (Array.Exists(BASE_STRING_TYPE, t => t == type))
7 {
8 json = string.Format("\"{0}\"", obj);
9 }
10 else
11 {
12 AbstractConverter converter;
13 if (type.IsArray)
14 {
15 //數組轉化
16 }
17 else if (type.IsGenericType)
18 {
19 var argCount = type.GetGenericArguments().Length;
20 if (argCount == 1)
21 {
22 //泛型列表轉化
23 }
24 else
25 {
26 //泛型字典轉化
27 }
28 }
29 else
30 {
31 //普通類轉化
32 }
33 json = converter.ToJson();
34 }
35 return json;
數組、泛型轉化示例代碼如下:

1 --數組
2 var array = arrObj as Array;
3 var arrJSON = new string[array.Length];
4 var index = 0;
5 foreach (var obj in array)
6 {
7 arrJSON[index++] = ToJSON(obj.GetType(), obj);
8 }
9
10 --泛型列表
11 var list = objList as IList;
12 var arrJSON = new string[list.Count];
13 for (int i = 0; i < list.Count; i++)
14 {
15 arrJSON[index++] = ToJSON(list[i].GetType(), list[i]);
16 }
17
18 --泛型字典
19
20 var dic = dicObj as IDictionary;
21 //該類型數組下標1為鍵的類型,下標2為值的類型
22 var arrType = dicObj.GetType().GetGenericArguments();
23 var arrJSON = new string[dic.Count];
24 var index = 0;
25 foreach (DictionaryEntry entry in dic)
26 {
27 var name = ToJSON(arrType[0], entry.Key);
28 var value = ToJSON(arrType[1], entry.Value);
29 arrJSON[index++] = string.Concat(name, ":", value);
30 }
到這里大家可能就會有疑惑了,講了大半天怎么還沒有篩選功能出現呢,因為我們要先把大致的功能都准備好,然后加入篩選的話,才會更容易。從以上的編碼的流程我們可以發現,所有最終的轉化,都會回到普通對象遍歷屬性的地方,於是當我們要加入對相關屬性進行過濾控制的時候,只需要在遍歷屬性的地方進行判斷即可。
大致代碼如下:

1 public string ToJSON(object obj, params string[] arrFilter)
2 {
3 foreach (var property in obj.GetType().GetProperties(BindingFlags.GetProperty | BindingFlags.Instance | BindingFlags.Public))
4 {
5 if (tarrFilter != null && 0 < arrFilter.Length && Array.Exists(arrFilter, nm => nm == property.Name))
6 {
7 //轉化代碼
8 }
9 }
10 }
基本上的需求差不多完成了,但是仍然有一些疑惑,如果轉換的時候,我們想要的屬性比不想要屬性要多得多時,那樣傳入過濾的屬性名就會非常多,那樣也比較麻煩,於是我們就要在前面的基礎上再加入轉換除了傳入的屬性名以外的其他屬性。那我們可以將占換功能分為1、全部轉化 2、只轉化傳入的部分屬性名 3、轉化除了傳入的屬性名,我們可以用一個枚舉來表示。
枚舉代碼如下:

1 public enum EnumConversion
2 {
3 /// <summary>
4 /// 全部
5 /// </summary>
6 All,
7 /// <summary>
8 /// 其中一些
9 /// </summary>
10 Any,
11 /// <summary>
12 /// 除外(只轉化除傳入屬性以外的其他屬性)
13 /// </summary>
14 Except
15 }
然后稍微調整一下判斷的代碼,示例代碼如下:

1 public string ToJSON(object obj, EnumConversion conversion, params string[] arrFilter)
2 {
3 foreach (var property in obj.GetType().GetProperties(BindingFlags.GetProperty | BindingFlags.Instance | BindingFlags.Public))
4 {
5 var format = false;
6 if (tarrFilter != null && 0 < arrFilter.Length)
7 {
8 format = Array.Exists(arrFilter, nm => nm == property.Name);
9 }
10 if (conversion == EnumConversion.Except)
11 {
12 format = !format;
13 }
14 if (format)
15 {
16 //轉化代碼
17 }
18 }
19 }
寫到這里,一個比較完成的JSON轉化類就完成了,這樣子可以根據需求調整轉化的屬性,從而縮短JSON的字符串量,如果大家有什么樣更好的方法,請分享一下,有不足之處請指出,謝謝。