很多项目都会用到Json,而且大部分的Json都是格式固定,功能强大,转换简单等,标准的key,value集合字符串;直接JsonConvert.SerializeObject(List<T>)就可以搞定了,但凡事并非千篇一律,比如有的时候我们需要的Json可能只需要value,不需要key,并且前后可能还需要辅助信息等等,那该怎么办呢?我所能想到的可能有两种方案,1.自定义跟所需json格式一样的数据结构model,然后用JsonConvert.SerializeObject(model)直接得到;2.解析泛型集合再自定义拼接Json字符串。
自定义Json格式:Json格式比较灵活,可以随意定义,也可以无限嵌套,这里只取两层,所有的data和id构成rows的集合,其中data只取T的value,并且在data之前加一个id,所以这里的Json就相当于只有rows嵌套了data。
构造泛型T的一个实例:既然是泛型,那就具有通用性,所以这里随便构造一个model,除了字段的多少和数据类型对本反射和Expression有一点点影响,其他的几乎可以忽略不计。
public class Model1 { public int Sales1 { get; set; } public int Title1 { get; set; } public double Sales { get; set; } public string Title { get; set; } public string Author { get; set; } public decimal Price { get; set; } public string Store { get; set; } public string Shipping { get; set; } public string Bestseller { get; set; } public DateTime Publication { get; set; } }
Expression Tree核心代码:Expression解析T的成员变量,配合反射创建T的实例,用Expression动态创建Lambda表达式得到T的属性值。
public class ExpressionTreeProperty<T> { internal static Func<object, string, object> s_GetValue = null; static ExpressionTreeProperty() { if (s_GetValue == null) { s_GetValue = GenerateGetValue(); } } public object GetValue(T instance, string memberName) { return s_GetValue(instance, memberName); } public object GetValue(object instance, string memberName) { return s_GetValue(instance, memberName); } private static Func<object, string, object> GenerateGetValue() { var type = typeof(T); var instance = Expression.Parameter(typeof(object), "instance"); var memberName = Expression.Parameter(typeof(string), "memberName"); var nameHash = Expression.Variable(typeof(int), "nameHash"); var getHashCode = Expression.Assign(nameHash, Expression.Call(memberName, typeof(object).GetMethod("GetHashCode"))); var switchEx = Expression.Switch(nameHash, Expression.Constant(null), (from propertyInfo in type.GetProperties(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public) let property = Expression.Property(Expression.Convert(instance, typeof(T)), propertyInfo.Name) let propertyHash = Expression.Constant(propertyInfo.Name.GetHashCode(), typeof(int)) select Expression.SwitchCase(Expression.Convert(property, typeof(object)), propertyHash)).ToArray()); var methodBody = Expression.Block(typeof(object), new[] { nameHash }, getHashCode, switchEx); return Expression.Lambda<Func<object, string, object>>(methodBody, instance, memberName).Compile(); } }
拼接Json字符串:
/// <summary> /// Reflection解析List<T>拼接Json字符串 /// </summary> public static string ListToJsonOfReflection<T>(IList<T> list) { var json = new StringBuilder(@"{ rows: ["); ; i < list.Count; i++) { json.Append("{ id :"); json.Append(i + + ", data:["); var pi = list[i].GetType().GetProperties(); foreach (var property in pi) { json.Append("\"" + property.GetValue(list[i], null) + "\","); } json = json.Remove(json.Length - , ); json.Append("]},"); } json = json.Remove(json.Length - , ); json.Append("]}"); return json.ToString(); } /// <summary> /// Expression解析List<T>拼接Json字符串 /// </summary> public static string ListToJsonOfExpression<T>(IList<T> list) { var json = new StringBuilder(@"{ rows: ["); var expressionTreeProperty= new ExpressionTreeProperty<Model1>(); ; i < list.Count; i++) { var propertys = list[i].GetType().GetProperties(); json.Append("{ id :"); json.Append(i + + ", data:["); foreach (var property in propertys) { json.Append("\"" + expressionTreeProperty.GetValue(list[i], property.Name) + "\","); } json = json.Remove(json.Length - , ); json.Append("]},"); } json = json.Remove(json.Length - , ); json.Append("]}"); return json.ToString(); }
测试用例:用上面的泛型实例model1构造一个容量为10万个集合List<T>,至于T的属性值随便给他赛一些就可以了。
public void TestMethod1() { var model = new List<Model1>(); Stopwatch stopwatch = new Stopwatch(); stopwatch.Start(); ; i < ; i++) { model.Add(new Model1 { Sales1 = , Title1 = , Sales = + i, Title = "标题" + i, Shipping = ", Author = "作者" + i, Store = ", Bestseller = ", Price = i, Publication = DateTime.Now }); } stopwatch.Start(); var json = JsonConvert.SerializeObject(model); stopwatch.Stop(); stopwatch.Stop(); Console.WriteLine(@"Json.NET按默认格式直接序列化100000个泛型对象耗时:" + stopwatch.ElapsedMilliseconds + @"毫秒"); stopwatch.Restart(); var reflectionJson = ListToJsonOfReflection<Model1>(model); stopwatch.Stop(); Console.WriteLine(@"Reflection解析自定义拼接序列化100000个泛型对象耗时:" + stopwatch.ElapsedMilliseconds + @"毫秒"); stopwatch.Restart(); var expressionJson = ListToJsonOfExpression<Model1>(model); stopwatch.Stop(); Console.WriteLine(@"Expression Tree解析自定义拼接序列化100000个泛型对象耗时:" + stopwatch.ElapsedMilliseconds + @"毫秒"); }
顺便做个性能测试,当然JsonConvert.SerializeObject(List<T>)得到的是key和value的集合字符串,比本文所需要的自定义拼接的字符串多了key,所以没有什么可比性,只是顺带列出来而已。纯反射解析List<T>拼接和Expression Tree解析List<T>拼接得到的Json格式一模一样。很明显Expression Tree 配合反射在执行大量动作时性能比纯反射还要高很多。