有关JSON的介绍,请参见http://www.json.org/json-zh.html
对于一个类,其中可能包括field, property,方法不做JSON序列化。我们可以去field和property进行JSON转化。
模仿反射中的FieldInfo和PropertyInfo, 两者都继承于MemberInfo,我们定义三个类,用来存储field,property的信息:
JsonMemberInfo.cs
using System; namespace XXX.JsonSerializer { public class JsonMemberInfo { protected JsonAttrribute attribute; public JsonAttrribute Attribute { get { return this.attribute; } set { this.attribute = value; } } } }
JsonFieldInfo.cs
using System; using System.Collections.Generic; using System.Text; using System.Reflection; namespace XXX.JsonSerializer { public class JsonFieldInfo : JsonMemberInfo { private FieldInfo info; public FieldInfo Info { get { return this.info; } set { this.info = value; } } public string JsonName { get { if ((this.attribute != null) && (this.attribute.JsonName != string.Empty)) { return this.attribute.JsonName; } else { return this.info.Name; } } } public JsonFieldInfo(FieldInfo i) { this.info = i; } } }
JsonPropertyInfo.cs
using System; using System.Collections.Generic; using System.Text; using System.Reflection; namespace XXX.JsonSerializer { public class JsonPropertyInfo : JsonMemberInfo { private PropertyInfo info; public PropertyInfo Info { get { return this.info; } set { this.info = value; } } public string JsonName { get { if ((this.attribute != null) && (this.attribute.JsonName != string.Empty)) { return this.attribute.JsonName; } else { return this.info.Name; } } } public JsonPropertyInfo(PropertyInfo i) { this.info = i; } } }
接下来,我们用一个类来保存被JSON的对象的内容:
JsonClassInfo.cs
using System; namespace XXX.JsonSerializer { public class JsonClassInfo { private Type classType; private JsonFieldInfo[] fields; private JsonPropertyInfo[] properties; public JsonClassInfo(Type t) { if (t == null) { throw new Exception("Json Serializer cannot get the type of the object!"); } this.classType = t; } public string ClassName { get { return this.classType.Name; } } public Type ClassType { get { return this.classType; } } public JsonFieldInfo[] ClassFields { get { return this.fields; } set { this.fields = value; } } public JsonPropertyInfo[] ClassProperties { get { return this.properties; } set { this.properties = value; } } } }
所有被JSON的对象的field, property的信息会保存在这个类中。
还要指定一个对象中哪些field需要被JSON,以及提供JSON序列化时的控制。我们用到Attribute这个东东:
JsonAttrribute.cs
using System;
namespace XXX.JsonSerializer { [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)] public class JsonAttrribute : Attribute { private string name; private bool isIgnore; public JsonAttrribute(bool ignore) : this(ignore, string.Empty) { } public JsonAttrribute(string name) : this(false, name) { } public JsonAttrribute() : base() { } public JsonAttrribute(bool ignore, string name) { this.isIgnore = ignore; this.name = name; } public string JsonName { get { return this.name; } set { this.name = value; } } public bool IsIgnore { get { return this.isIgnore; } } } }
下面上重头戏,JSON序列化的实现:
JsonSerializer.cs
using System; using System.Collections; using System.Collections.Generic; using System.Reflection; using System.Text; namespace XXX.JsonSerializer { public class JsonSerializer { private const string LeftBrace = "{"; private const string RightBrace = "}"; private const string LeftBracket = "["; private const string RightBracket = "]"; private const string Comma = ", "; private static readonly Type stringType = typeof(string); private static readonly Type dateTimeType = typeof(DateTime); private static readonly Type jsonAttrribute = typeof(JsonAttrribute); /// <summary> /// /// </summary> /// <param name="obj"></param> /// <returns></returns> public string Serialize(object obj) { if (null == obj) { return string.Empty; } Type objType = obj.GetType();
#region Serialize an Array if (objType.IsArray) { StringBuilder sb = new StringBuilder(); sb.Append(LeftBracket); IEnumerable enumer = obj as IEnumerable; if (enumer != null) { bool extraComma = false; IEnumerator enumrator = enumer.GetEnumerator(); while (enumrator.MoveNext()) { //recursive serialize object in array sb.Append(this.Serialize(enumrator.Current)); sb.Append(Comma); extraComma = true; } if (extraComma) { sb.Remove(sb.Length - Comma.Length, Comma.Length); } } sb.Append(RightBracket); return sb.ToString(); } #endregion if (objType.IsEnum) { return string.Format("'{0}'", obj.ToString()); } if (objType.IsValueType) { if (objType == dateTimeType) { return string.Format("'{0}'", EscapeCharacter(obj.ToString())); } // The input object is value type if (objType.IsPrimitive) { // The input object is int, doublt, etc. return string.Format("'{0}'", EscapeCharacter(obj.ToString())); } else { // The input object is struct. return this.SerializeClassStruct(obj, objType); } } else { // The input object is not value type if (objType == stringType) { // The input object is string return string.Format("'{0}'", EscapeCharacter(obj.ToString())); } else if (objType.IsClass) { // The input object is a customize class. return this.SerializeClassStruct(obj, objType); } else { // Ingore delegate and interface. return string.Empty; } } } /// <summary> /// /// </summary> /// <param name="obj"></param> /// <returns></returns> private string SerializeClassStruct(object obj, Type objType) { StringBuilder sb = new StringBuilder(); sb.Append(LeftBrace); bool extraComma = false; string strValue = string.Empty; JsonClassInfo classInfo = this.ReflectClassInfo(objType); foreach (JsonFieldInfo info in classInfo.ClassFields) { strValue = GetValue(obj, info.Info, info.JsonName, ref extraComma); sb.Append(strValue); } foreach (JsonPropertyInfo info in classInfo.ClassProperties) { strValue = GetValue(obj, info.Info, info.JsonName, ref extraComma); sb.Append(strValue); } if (extraComma) { sb.Remove(sb.Length - Comma.Length, Comma.Length); } sb.Append(RightBrace); return sb.ToString(); } /// <summary> /// /// </summary> /// <param name="objType"></param> /// <param name="obj"></param> /// <param name="fieldInfo"></param> /// <param name="jsonName"></param> /// <param name="extraComma"></param> /// <returns></returns> private string GetValue(object obj, FieldInfo info, string jsonName, ref bool extraComma) { object value = null; StringBuilder sb = new StringBuilder(); if (obj == null) { return string.Empty; } if (info != null) { value = info.GetValue(obj); //According to XmlSerializer, if an object is null, it will NOT be serializered if (value != null) { if (!string.IsNullOrEmpty(jsonName)) { sb.Append(string.Format("'{0}':", this.EscapeCharacter(jsonName))); } sb.Append(Serialize(value)); sb.Append(Comma); extraComma = true; } } return sb.ToString(); } /// <summary> /// /// </summary> /// <param name="objType"></param> /// <param name="obj"></param> /// <param name="fieldInfo"></param> /// <param name="jsonName"></param> /// <param name="extraComma"></param> /// <returns></returns> private string GetValue(object obj, PropertyInfo info, string jsonName, ref bool extraComma) { object value = null; StringBuilder sb = new StringBuilder(); if (info != null) { try { value = info.GetValue(obj, null); } catch { // in case the property is indexer. // It will igorne the indexer value = ""; } //According to XmlSerializer, if an object is null, it will NOT be serializered if (value != null) { if (!string.IsNullOrEmpty(jsonName)) { sb.Append(string.Format("'{0}':", this.EscapeCharacter(jsonName))); } sb.Append(Serialize(value)); sb.Append(Comma); extraComma = true; } } return sb.ToString(); } private JsonClassInfo ReflectClassInfo(Type objType) { // //We could use cache to store reflected object // FieldInfo[] fields = objType.GetFields(BindingFlags.GetField | BindingFlags.GetProperty | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public); JsonClassInfo classInfo = new JsonClassInfo(objType); classInfo.ClassFields = new JsonFieldInfo[fields.Length]; for (int i = 0; i < fields.Length; ++i) { classInfo.ClassFields[i] = new JsonFieldInfo(fields[i]); object[] jsonInfo = fields[i].GetCustomAttributes(jsonAttrribute, true); if (jsonInfo != null && jsonInfo.Length > 0) { JsonAttrribute jAttri = jsonInfo[0] as JsonAttrribute; if (jAttri.IsIgnore) { continue; } classInfo.ClassFields[i].Attribute = jAttri; } } PropertyInfo[] properties = objType.GetProperties(BindingFlags.GetField | BindingFlags.GetProperty | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public); classInfo.ClassProperties = new JsonPropertyInfo[properties.Length]; for (int i = 0; i < properties.Length; ++i) { classInfo.ClassProperties[i] = new JsonPropertyInfo(properties[i]); object[] jsonInfo = properties[i].GetCustomAttributes(jsonAttrribute, true); if (jsonInfo != null && jsonInfo.Length > 0) { JsonAttrribute jAttri = jsonInfo[0] as JsonAttrribute; if (jAttri.IsIgnore) { continue; } classInfo.ClassProperties[i].Attribute = jAttri; } } return classInfo; } /// <summary> /// Escape \ & ' /// </summary> /// <param name="input"></param> /// <returns></returns> public string EscapeCharacter(string input) { return input.Replace(@"\", @"\\").Replace("'", @"\'"); } } }
通过反射,可以得到给定object里的field, property信息,然后对不同情况进行处理。
值得注意的是对象中嵌套对象需要用到递归;对数组,对象,值,在JSON序列化要区分对待;对特殊字符要转义。
使用示例:
class Program { static void Main(string[] args) { TestClass tc = new TestClass(); JsonSerializer ser = new JsonSerializer(); string json = ser.Serialize(tc); Console.WriteLine(json); } } class TestClass { [JsonAttrribute("NC")] public NestClass nc = new NestClass(); //This field will not be serialized because ingore = true [JsonAttrribute(true)] NestClass ic = new NestClass(); public int[] IntegerArray = new int[] {1, 2, 3 }; //private field will not be serialized [JsonAttrribute("PNC")] private NestClass pnc = new NestClass(); } class NestClass { [JsonAttrribute("NF")] public string NestClassFiled = "NestClassFiled"; }
输出如下:
{'NC':{'NF':'NestClassFiled'}, 'IntegerArray':['1', '2', '3']}
新版本的.net framework中提供了把对象转成JSON的方法,在一个dll里,具体叫什么记不得了,Scottegu的blog中提到过。