有关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中提到过。