序列化对大家来说应该都不陌生,特别是现在大量使用WEBAPI,JSON满天飞,序列化操作应该经常出现在我们的代码上。
而我们最常用的序列化工具应该就是Newtonsoft.Json,当然你用其它工具类也是没问题的,我们重点讲的不是这个工具,我们的重点是高效的可自定义控制的序列化操作。
首先我们说一下大致的序列化原理:
一般情况下,我们把一个实体类,或是数据列表传给工具类(这里我拿Newtonsoft.Json做例子,其它的也是类似的)如:
class ClassTest{
public string aa{get;set;}
public string bb{get;set;}
}
var jsonStr=Newtonsoft.Json.JsonConvert.SerializeObject(new ClassTest());
得到的jsonStr="{\"aa\":null,\"bb\":null}";
这个时候我们就在想,序列化是怎么完成的呢,为什么我只要给他一个类,他就能帮我转化成一个JSON字符串呢?(应该有点开发年头的老鸟心里都有数,我还是多啰嗦几句,给新人看一下)
其实默认情况下它是使用了反射,当我们得到一个类,想知道它有什么成员,以及成员分别是什么值的时候,我们就只能用反射来得到,通过元数据我们能得到任何我们想要的信息(不了解反射的朋友可以去查一下相关资料),反射用起来确实很方便,但是有个很大的问题,就是性能,反射是相当耗性能的,下面我会讲到怎么样不用反射来实现序列化。
因为我们今天讲的重点是自定义,所以我们先来谈谈如何自定义。
默认情况下序列化工具会反射我们所有的属性,然后将它们转化到字符串中,如果我们只想让部分属性序列化出来要怎么做呢?
可能有人会说,那我再建立一个类,里面只写部分属性不就好了,虽然这也是一个办法,不过我相信大家都知道这个方法有多LOW,因为我们的需求是不断变化的,可能我们一个类要应付几十种场景,不可能我们每个场景都要新建一类吧,而且后期需求还会变动,还会增加,所以这个方案不行(如果你只想应付一下老板这个方法还是行的...)
可能又有人说,这些序列化工具类都提供了对应的特性,只要我写在我的属性身上我就可以控制哪些显示,哪些不显示,哪些有什么默认值了比如:
[JsonProperty]
[JsonIgnore]
[DefaultValue(30)]
但是我想说的是你还是不能解决我们刚刚说的问题,因为一个类特性你只能写一次,你这样做虽然比刚刚好了一点,但是应付多个场景依然不行。
我们想要的解决方案是可以由我们程序*控制哪些类本次需要在序列化字符串中,哪些不需要,我们只需要一个类就可以解决所有场景的问题,*控制。
于是乎我们找到了一个方案 使用 ISerializable ,用它我们可以完全自定义序列化过程中的具体行为,不再需要反射来帮我们,我们自己来实现序列化的细节,这是最高效,最*的一种方式,但是它也有一个缺点
就是过于繁琐。还是拿上面的类来做个例子:
class ClassTest: ISerializable{
#region json序列化 反序列化
/// <summary>序列化时调用</summary>
public virtual void GetObjectData(SerializationInfo s, StreamingContext c)
{
if(!string.IsNullOrEmpty(aa)) //这里可以使用任何条件来控制是否序列化
{s.AddValue("aa", aa);}
s.AddValue("bb", bb);
}
/// <summary>反序列化时操作</summary>
protected ClassTest(SerializationInfo s, StreamingContext c)
{
SerializationInfoEnumerator sItem = s.GetEnumerator();
while (sItem.MoveNext())
{
switch (sItem.Name)
{
case "aa": aa = s.GetString("aa"); break;
case "bb": bb = s.GetString("bb"); break;
default: break;
}
}
}
#endregion
public string aa{get;set;}
public string bb{get;set;}
}
只需要继承这个接口,实现两个方法,就可以完全的自定义序列化过程了
可能又有人说你这里只说了JSON,还有XML序列化怎么办呢?不要急,XML也可以自定义,只要实现 IXmlSerializable,同样实现它里面定义的方法就可以了,我这里就不详细说了。
我们这样做了以后序列化的时候它就不会使用反射了,而是直接调用相应的接口方法,这样性能显著提高,同时你又可以根据自己的情况自定义序列的规则。
那我们刚刚说了它有一个缺点就是写起来太繁琐了,那我们怎么办呢,其实我是一直都有代码生成器,我写好了模版只要设计好数据库模型,我的实体类就自动生成出来了,根本不需要打任何代码,所以对我来说也不繁琐.
还有一个问题大家应该注意到了就是我所有的序列化判断是在类的方法里面写的,如果我想在外部调用的时候来控制哪些字段序列化,哪些不序列化怎么办呢,你这个也解决不了啊?
对,上面的例子确实解决不了,但是我还是有解决方案,我利用泛型把实体类做了改造,这样我就可以完全在外部来控制序列化了,这个具体的实现我会在后期的文章中在介绍我自己设计的开发框架时再详细说(如果大家有兴趣看的话)
说了半天好像完全没提到.NET core,现在要说它了,上面好个方案是可以用到之前的.net framework的各版本的,以及mono上也是可以运行的,但是现在最新的.NET core 就有点问题了,我最近想把我的开发框架
移植到.NET core上就出现了问题,它不支持ISerializable,本来我还以为是我用错了,后来查看了源码才发现是真的还不支持
因为.NET core才正式发布没多久,很多东西还不不是很完善,我在查这个问题的时候也是找了半天原因,所以我在这里也是给大家提个醒,这个东西暂时还没弄好,不过后面应该会弄好的,时间问题。
那么现阶段我们要怎么处理呢,其实很简单,既然序列化是把我们的类属生拼接成一个字符串,为什么我们不自己实现这个方法呢,只要用代码生成器帮我们生成这段代码,对我们来说也是很方便,高效的,应该说比以上方法都要高效。比如:
class ClassTest{
public string aa{get;set;}
public string bb{get;set;}
public string ToJson()
{
System.Text.StringBuilder sb = new System.Text.StringBuilder("{");
if(!string.IsNullOrEmpty(aa)) //这里可以使用任何条件来控制是否序列化
{sb.AppendFormat(",\"aa\":{0}", aa);}
sb.AppendFormat(",\"bb\":{0}", bb);
sb.Append("}");
return sb.ToString();
}
}
就这样写个方法就可以了,需要用到序列化的地方只需要用 new ClassTest().ToJson()就可以了,这样也很方便吧,同样的你想要外部调用时使用哪些序列化,后期我会再详细讲
其实小弟第一次发博客写文章,写的东西也很简单,没什么太大的技术含量,希望各位大神,大虾指点我的时候轻点拍砖,我也只是把我知道的一直在用的,感觉比较好用的分享出来而以,并不是来秀技术的,我也没什么技术好秀。
以上内容,完全手打原创,转载请注明出处!望各位大神指错!