1. 程式人生 > >Object轉化成JSON字串

Object轉化成JSON字串

有關JSON的介紹,請參見http://www.json.org/json-zh.html

對於一個類,其中可能包括field, property,方法不做JSON序列化。我們可以去field和property進行JSON轉化。
模仿反射中的FieldInfo和PropertyInfo, 兩者都繼承於MemberInfo,我們定義三個類,用來儲存field,property的資訊:

JsonMemberInfo.cs
  1. using System;
  2. namespace ×××.JsonSerializer
  3. {
  4. publicclass JsonMemberInfo
  5.     {
  6. protected JsonAttrribute attribute;
  7. public
     JsonAttrribute Attribute
  8.         {
  9. get
  10.             {
  11. returnthis.attribute;
  12.             }
  13. set
  14.             {
  15. this.attribute = value;
  16.             }
  17.         }
  18.     }
  19. }
JsonFieldInfo.cs
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Text;
  4. using System.Reflection;
  5. namespace ×××.JsonSerializer
  6. {
  7. publicclass JsonFieldInfo : JsonMemberInfo
  8.     {
  9. private FieldInfo info;
  10. public FieldInfo Info
  11.         {
  12. get
  13.             {
  14. returnthis.info;
  15.             }
  16. set
  17.             {
  18. this.info = value;
  19.             }
  20.         }
  21. publicstring JsonName
  22.         {
  23. get
  24.             {
  25. if ((this.attribute != null) && (this.attribute.JsonName != string.Empty))
  26.                 {
  27. returnthis.attribute.JsonName;
  28.                 }
  29. else
  30.                 {
  31. returnthis.info.Name;
  32.                 }
  33.             }
  34.         }
  35. public JsonFieldInfo(FieldInfo i)
  36.         {
  37. this.info = i;
  38.         }
  39.     }
  40. }
JsonPropertyInfo.cs
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Text;
  4. using System.Reflection;
  5. namespace ×××.JsonSerializer
  6. {
  7. publicclass JsonPropertyInfo : JsonMemberInfo
  8.     {
  9. private PropertyInfo info;
  10. public PropertyInfo Info
  11.         {
  12. get
  13.             {
  14. returnthis.info;
  15.             }
  16. set
  17.             {
  18. this.info = value;
  19.             }
  20.         }
  21. publicstring JsonName
  22.         {
  23. get
  24.             {
  25. if ((this.attribute != null) && (this.attribute.JsonName != string.Empty))
  26.                 {
  27. returnthis.attribute.JsonName;
  28.                 }
  29. else
  30.                 {
  31. returnthis.info.Name;
  32.                 }
  33.             }
  34.         }
  35. public JsonPropertyInfo(PropertyInfo i)
  36.         {
  37. this.info = i;
  38.         }
  39.     }
  40. }
接下來,我們用一個類來儲存被JSON的物件的內容:

JsonClassInfo.cs
  1. using System;
  2. namespace ×××.JsonSerializer
  3. {
  4. publicclass JsonClassInfo
  5.     {
  6. private Type classType;
  7. private JsonFieldInfo[] fields;
  8. private JsonPropertyInfo[] properties;
  9. public JsonClassInfo(Type t)
  10.         {
  11. if (t == null)
  12.             {
  13. thrownew Exception("Json Serializer cannot get the type of the object!");
  14.             }
  15. this.classType = t;
  16.         }
  17. publicstring ClassName
  18.         {
  19. get
  20.             {
  21. returnthis.classType.Name;
  22.             }
  23.         }
  24. public Type ClassType
  25.         {
  26. get
  27.             {
  28. returnthis.classType;
  29.             }
  30.         }
  31. public JsonFieldInfo[] ClassFields
  32.         {
  33. get
  34.             {
  35. returnthis.fields;
  36.             }
  37. set
  38.             {
  39. this.fields = value;
  40.             }
  41.         }
  42. public JsonPropertyInfo[] ClassProperties
  43.         {
  44. get
  45.             {
  46. returnthis.properties;
  47.             }
  48. set
  49.             {
  50. this.properties = value;
  51.             }
  52.         }
  53.     }
  54. }
所有被JSON的物件的field, property的資訊會儲存在這個類中。

還要指定一個物件中哪些field需要被JSON,以及提供JSON序列化時的控制。我們用到Attribute這個東東:

JsonAttrribute.cs
  1. using System;
  2. namespace ×××.JsonSerializer
  3. {
  4.     [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)]
  5. publicclass JsonAttrribute : Attribute
  6.     {
  7. privatestring name;
  8. privatebool isIgnore;
  9. public JsonAttrribute(bool ignore)
  10.             : this(ignore, string.Empty)
  11.         {
  12.         }
  13. public JsonAttrribute(string name)
  14.             : this(false, name)
  15.         {
  16.         }
  17. public JsonAttrribute() : base() { }
  18. public JsonAttrribute(bool ignore, string name)
  19.         {
  20. this.isIgnore = ignore;
  21. this.name = name;
  22.         }
  23. publicstring JsonName
  24.         {
  25. get
  26.             { 
  27. returnthis.name; 
  28.             }
  29. set
  30.             {
  31. this.name = value;
  32.             }
  33.         }
  34. publicbool IsIgnore
  35.         {
  36. get { returnthis.isIgnore; }
  37.         }
  38.     }
  39. }
我們設定只有Field和Proptery才能被JSON序列化。在過載方法中,如果IsIgnore被設定成true,則該欄位不會被JSON,也可以設定JsonName來指定JSON後該物件的名稱。

下面上重頭戲,JSON序列化的實現:

JsonSerializer.cs
  1. using System;
  2. using System.Collections;
  3. using System.Collections.Generic;
  4. using System.Reflection;
  5. using System.Text;
  6. namespace ×××.JsonSerializer
  7. {
  8. publicclass JsonSerializer
  9.     {
  10. privateconststring LeftBrace = "{";
  11. privateconststring RightBrace = "}";
  12. privateconststring LeftBracket = "[";
  13. privateconststring RightBracket = "]";
  14. privateconststring Comma = ", ";
  15. privatestaticreadonly Type stringType = typeof(string);
  16. privatestaticreadonly Type dateTimeType = typeof(DateTime);
  17. privatestaticreadonly Type jsonAttrribute = typeof(JsonAttrribute);
  18. /// <summary>
  19. /// 
  20. /// </summary>
  21. /// <param name="obj"></param>
  22. /// <returns></returns>
  23. publicstring Serialize(object obj)
  24.         {
  25. if (null == obj)
  26.             {
  27. returnstring.Empty;
  28.             }
  29.             Type objType = obj.GetType();
  30.             #region Serialize an Array
  31. if (objType.IsArray)
  32.             {
  33.                 StringBuilder sb = new StringBuilder();
  34.                 sb.Append(LeftBracket);
  35.                 IEnumerable enumer = obj as IEnumerable;
  36. if (enumer != null)
  37.                 {
  38. bool extraComma = false;
  39.                     IEnumerator enumrator = enumer.GetEnumerator();
  40. while (enumrator.MoveNext())
  41.                     {
  42. //recursive serialize object in array
  43.                         sb.Append(this.Serialize(enumrator.Current));
  44.                         sb.Append(Comma);
  45.                         extraComma = true;
  46.                     }
  47. if (extraComma)
  48.                     {
  49.                         sb.Remove(sb.Length - Comma.Length, Comma.Length);
  50.                     }
  51.                 }
  52.                 sb.Append(RightBracket);
  53. return sb.ToString();
  54.             }
  55.             #endregion
  56. if (objType.IsEnum)
  57.             {
  58. returnstring.Format("'{0}'", obj.ToString());
  59.             }
  60. if (objType.IsValueType)
  61.             {
  62. if (objType == dateTimeType)
  63.                 {
  64. returnstring.Format("'{0}'", EscapeCharacter(obj.ToString()));
  65.                 }
  66. //  The input object is value type
  67. if (objType.IsPrimitive)
  68.                 {
  69. //  The input object is int, doublt, etc.
  70. returnstring.Format("'{0}'", EscapeCharacter(obj.ToString()));
  71.                 }
  72. else
  73.                 {
  74. //  The input object is struct.
  75. returnthis.SerializeClassStruct(obj, objType);
  76.                 }
  77.             }
  78. else
  79.             {
  80. //  The input object is not value type
  81. if (objType == stringType)
  82.                 {
  83. //  The input object is string
  84. returnstring.Format("'{0}'", EscapeCharacter(obj.ToString()));
  85.                 }
  86. elseif (objType.IsClass)
  87.                 {
  88. //  The input object is a customize class.
  89. returnthis.SerializeClassStruct(obj, objType);
  90.                 }
  91. else
  92.                 {
  93. //  Ingore delegate and interface.
  94. returnstring.Empty;
  95.                 }
  96.             }
  97.         }
  98. /// <summary>
  99. /// 
  100. /// </summary>
  101. /// <param name="obj"></param>
  102. /// <returns></returns>
  103. privatestring SerializeClassStruct(object obj, Type objType)
  104.         {
  105.             StringBuilder sb = new StringBuilder();
  106.             sb.Append(LeftBrace);
  107. bool extraComma = false;
  108. string strValue = string.Empty;
  109.             JsonClassInfo classInfo = this.ReflectClassInfo(objType);
  110. foreach (JsonFieldInfo info in classInfo.ClassFields)
  111.             {
  112.                 strValue = GetValue(obj, info.Info, info.JsonName, ref extraComma);
  113.                 sb.Append(strValue);
  114.             }
  115. foreach (JsonPropertyInfo info in classInfo.ClassProperties)
  116.             {
  117.                 strValue = GetValue(obj, info.Info, info.JsonName, ref extraComma);
  118.                 sb.Append(strValue);
  119.             }
  120. if (extraComma)
  121.             {
  122.                 sb.Remove(sb.Length - Comma.Length, Comma.Length);
  123.             }
  124.             sb.Append(RightBrace);
  125. return sb.ToString();
  126.         }
  127. /// <summary>
  128. /// 
  129. /// </summary>
  130. /// <param name="objType"></param>
  131. /// <param name="obj"></param>
  132. /// <param name="fieldInfo"></param>
  133. /// <param name="jsonName"></param>
  134. /// <param name="extraComma"></param>
  135. /// <returns></returns>
  136. privatestring GetValue(object obj, FieldInfo info, string jsonName, refbool extraComma)
  137.         {
  138. object value = null;
  139.             StringBuilder sb = new StringBuilder();
  140. if (obj == null)
  141.             {
  142. returnstring.Empty;
  143.             }
  144. if (info != null)
  145.             {
  146.                 value = info.GetValue(obj);
  147. //According to XmlSerializer, if an object is null, it will NOT be serializered
  148. if (value != null)
  149.                 {
  150. if (!string.IsNullOrEmpty(jsonName))
  151.                     {
  152.                         sb.Append(string.Format("'{0}':"this.EscapeCharacter(jsonName)));
  153.                     }
  154.                     sb.Append(Serialize(value));
  155.                     sb.Append(Comma);
  156.                     extraComma = true;
  157.                 }
  158.             }
  159. return sb.ToString();
  160.         }
  161. /// <summary>
  162. /// 
  163. /// </summary>
  164. /// <param name="objType"></param>
  165. /// <param name="obj"></param>
  166. /// <param name="fieldInfo"></param>
  167. /// <param name="jsonName"></param>
  168. /// <param name="extraComma"></param>
  169. /// <returns></returns>
  170. privatestring GetValue(object obj, PropertyInfo info, string jsonName, refbool extraComma)
  171.         {
  172. object value = null;
  173.             StringBuilder sb = new StringBuilder();
  174. if (info != null)
  175.             {
  176. try
  177.                 {
  178.                     value = info.GetValue(obj, null);
  179.                 }
  180. catch
  181.                 {
  182. //  in case the property is indexer.
  183. //  It will igorne the indexer
  184.                     value = "";
  185.                 }
  186. //According to XmlSerializer, if an object is null, it will NOT be serializered
  187. if (value != null)
  188.                 {
  189. if (!string.IsNullOrEmpty(jsonName))
  190.                     {
  191.                         sb.Append(string.Format("'{0}':"this.EscapeCharacter(jsonName)));
  192.                     }
  193.                     sb.Append(Serialize(value));
  194.                     sb.Append(Comma);
  195.                     extraComma = true;
  196.                 }
  197.             }
  198. return sb.ToString();
  199.         }
  200. private JsonClassInfo ReflectClassInfo(Type objType)
  201.         {
  202. //
  203. //We could use cache to store reflected object 
  204. //
  205.             FieldInfo[] fields = objType.GetFields(BindingFlags.GetField | BindingFlags.GetProperty | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public);            
  206.             JsonClassInfo classInfo = new JsonClassInfo(objType);
  207.             classInfo.ClassFields = new JsonFieldInfo[fields.Length];
  208. for (int i = 0; i < fields.Length; ++i)
  209.             {
  210.                 classInfo.ClassFields[i] = new JsonFieldInfo(fields[i]);
  211. object[] jsonInfo = fields[i].GetCustomAttributes(jsonAttrribute, true);
  212. if (jsonInfo != null && jsonInfo.Length > 0)
  213.                 {
  214.                     JsonAttrribute jAttri = jsonInfo[0] as JsonAttrribute;
  215. if (jAttri.IsIgnore)
  216.                     {
  217. continue;
  218.                     }
  219.                     classInfo.ClassFields[i].Attribute = jAttri;
  220.                 }
  221.             }
  222.             PropertyInfo[] properties = objType.GetProperties(BindingFlags.GetField | BindingFlags.GetProperty | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public);
  223.             classInfo.ClassProperties = new JsonPropertyInfo[properties.Length];
  224. for (int i = 0; i < properties.Length; ++i)
  225.             {
  226.                 classInfo.ClassProperties[i] = new JsonPropertyInfo(properties[i]);
  227. object[] jsonInfo = properties[i].GetCustomAttributes(jsonAttrribute, true);
  228. if (jsonInfo != null && jsonInfo.Length > 0)
  229.                 {
  230.                     JsonAttrribute jAttri = jsonInfo[0] as JsonAttrribute;
  231. if (jAttri.IsIgnore)
  232.                     {
  233. continue;
  234.                     }
  235.                     classInfo.ClassProperties[i].Attribute = jAttri;
  236.                 }
  237.             }
  238. return classInfo;
  239.         }
  240. /// <summary>
  241. /// Escape / & '
  242. /// </summary>
  243. /// <param name="input"></param>
  244. /// <returns></returns>
  245. publicstring EscapeCharacter(string input)
  246.         {
  247. return input.Replace(@"/", @"//").Replace("'", @"/'");
  248.         }
  249.     }
  250. }
通過反射,可以得到給定object裡的field, property資訊,然後對不同情況進行處理。
值得注意的是物件中巢狀物件需要用到遞迴;對陣列,物件,值,在JSON序列化要區分對待;對特殊字元要轉義。

使用示例:
  1. class Program
  2. {
  3. staticvoid Main(string[] args)
  4.     {
  5.         TestClass tc = new TestClass();
  6.         JsonSerializer ser = new JsonSerializer();
  7. string json = ser.Serialize(tc);
  8.         Console.WriteLine(json);
  9.     }
  10. }
  11. class TestClass
  12. {
  13.     [JsonAttrribute("NC")]
  14. public NestClass nc = new NestClass();
  15. //This field will not be serialized because ingore = true
  16.     [JsonAttrribute(true)]
  17.     NestClass ic = new NestClass();
  18. publicint[] IntegerArray = newint[] {1, 2, 3 };
  19. //private field will not be serialized
  20.     [JsonAttrribute("PNC")]
  21. private NestClass pnc = new NestClass();
  22. }
  23. class NestClass
  24.     [JsonAttrribute("NF")]
  25. publicstring NestClassFiled = "NestClassFiled";
  26. }
輸出如下:
{'NC':{'NF':'NestClassFiled'}, 'IntegerArray':['1', '2', '3']}

新版本的.net framework中提供了把物件轉成JSON的方法,在一個dll裡,具體叫什麼記不得了,scottegu的blog中提到過。