C#中,编写可选择任意属性转化成JSON
因为个人在业余有尝试在做一个游戏项目,所有的功能都是使用ajax的,因此要使用JSON作为媒介,然而如果是使用集成的类库进行JSON的转化,带来的影响就是一个类在传输到页面上的时候,其实仅仅只是需要其中的几个属性而已,如果嵌套的层数比较多,例如:一个类中包含其他的类或泛型或数组,这样子,数据加起来以后多出了不少。也许有人会创建一些额外的类去来处理,那的确是可以解决这种问题,但是不同的功能所使用到的数据要是个有差别的话,那么增加类则就变成了一个无底的深渊了。
很早就有这个想法要把文章写出来,可能是自己比较懒吧,总是因为公司的工作、业余游戏的开发或是其他问题没能完成这个事情,今天终于下定决心把这份小小的心得写出来。
首先,我们从单一的对象说起,一个对象内有一些属性,首先设想这些属性都是C#内简单的类型,例如:Int16、Int32、Int64、bool、Guid、string、char、DateTime这些(如果缺少可根据转换后的内容自行分组),我是将Int16、Int32、Int64、bool和Guid、string、char、DateTime分成2个不同的类型组别,因为在转化的过程中,前一个分组不需要引号。
代码如下:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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,最后拼接在一起。
代码如下:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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的方法,代码如下:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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;
数组、泛型转化示例代码如下:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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 }
到这里大家可能就会有疑惑了,讲了大半天怎么还没有筛选功能出现呢,因为我们要先把大致的功能都准备好,然后加入筛选的话,才会更容易。从以上的编码的流程我们可以发现,所有最终的转化,都会回到普通对象遍历属性的地方,于是当我们要加入对相关属性进行过滤控制的时候,只需要在遍历属性的地方进行判断即可。
大致代码如下:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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、转化除了传入的属性名,我们可以用一个枚举来表示。
枚举代码如下:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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 }
然后稍微调整一下判断的代码,示例代码如下:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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的字符串量,如果大家有什么样更好的方法,请分享一下,有不足之处请指出,谢谢。