简单,是因为只有一个类
轻量,是因为整个类代码只有300行
灵活,是因为扩展方式只需要继承重写某个方法即可
首先我将这个类称之为JsonBuilder,我希望它以StringBuilder的方式来实现Json字符串的转换
public class JsonBuilder { protected StringBuilder Buff = new StringBuilder(4096);//字符缓冲区 public string ToJsonString(object obj) { ....... return Buff.ToString(); } ....... }
然后我为希望为每一个基础类型单独完成一个方法,并且方法可以被重写
protected virtual void AppendByte(Byte value) protected virtual void AppendDecimal(Decimal value) protected virtual void AppendInt16(Int16 value) protected virtual void AppendInt32(Int32 value) protected virtual void AppendInt64(Int64 value) protected virtual void AppendSByte(SByte value) protected virtual void AppendUInt16(UInt16 value) protected virtual void AppendUInt32(UInt32 value) protected virtual void AppendUInt64(UInt64 value) protected virtual void AppendBoolean(Boolean value) protected virtual void AppendChar(Char value) protected virtual void AppendString(String value) protected virtual void AppendDateTime(DateTime value) protected virtual void AppendGuid(Guid value) protected virtual void AppendDouble(Double value) protected virtual void AppendSingle(Single value) protected virtual void AppendEnum(Enum value)
为了使子类重写时更方便,我将数字类型合并为一个
protected virtual void AppendNumber(IConvertible number)
但仍然保留原有方法,只是原有方法直接调用AppendNumber,就像这样
protected virtual void AppendByte(Byte value) { AppendNumber(value); } protected virtual void AppendDecimal(Decimal value) { AppendNumber(value); } protected virtual void AppendInt16(Int16 value) { AppendNumber(value); } protected virtual void AppendInt32(Int32 value) { AppendNumber(value); } protected virtual void AppendInt64(Int64 value) { AppendNumber(value); } protected virtual void AppendSByte(SByte value) { AppendNumber(value); } protected virtual void AppendUInt16(UInt16 value) { AppendNumber(value); } protected virtual void AppendUInt32(UInt32 value) { AppendNumber(value); } protected virtual void AppendUInt64(UInt64 value) { AppendNumber(value); } protected virtual void AppendDouble(Double value) { AppendNumber(value); } protected virtual void AppendSingle(Single value) { AppendNumber(value); }
这样的好处是我可以在子类中灵活的选择重写全部的数字类型,还是只重写某种特定类型
然后接着,我需要完成一些已知类型的转换方法,比如数组,集合,字典,数据表等等
protected virtual void AppendArray(IEnumerable array)//数组,集合 protected virtual void AppendJson(IDictionary dict)//字典 protected virtual void AppendDataSet(DataSet dataset)//数据表集 protected virtual void AppendDataTable(DataTable table)//单表 protected virtual void AppendDataView(DataView view)//表视图
ps:这些方法,单个实现都不困难,限于篇幅问题,这里就不介绍了,最后会放出完整代码
已知类型处理完后,再添加一个对未知类型的处理方法,这里我用的是最基本的反射
protected virtual void AppendOther(object obj) { Type t = obj.GetType(); Buff.Append('{'); string fix = ""; foreach (var p in t.GetProperties()) { if (p.CanRead) {
Buff.Append(fix); AppendKey(p.Name, false); object value = p.GetValue(obj, null); AppendObject(value); fix = ","; } } Buff.Append('}'); }
实际上有2个方法是目前为止不存在的
现在我们把他加上
/// <summary> 追加Key /// </summary> /// <param name="key"></param> /// <param name="escape">key中是否有(引号,回车,制表符等)特殊字符,需要转义</param> protected virtual void AppendKey(string key, bool escape) { if (escape) { AppendString(key); } else { Buff.Append('"'); Buff.Append(key); Buff.Append('"'); } Buff.Append(':'); } private Dictionary<object, object> _LoopObject = new Dictionary<object, object>();//循环引用对象缓存区 //泛对象 protected void AppendObject(object obj) { if (obj == null) Buff.Append("null");else if (obj is String) AppendString((String)obj); else if (obj is Int32) AppendInt32((Int32)obj); else if (obj is Boolean) AppendBoolean((Boolean)obj); else if (obj is DateTime) AppendDateTime((DateTime)obj); else if (obj is Double) AppendDouble((Double)obj); else if (obj is Enum) AppendEnum((Enum)obj); else if (obj is Decimal) AppendDecimal((Decimal)obj) ; else if (obj is Char) AppendChar((Char)obj); else if (obj is Single) AppendSingle((Single)obj); else if (obj is Guid) AppendGuid((Guid)obj); else if (obj is Byte) AppendByte((Byte)obj); else if (obj is Int16) AppendInt16((Int16)obj); else if (obj is Int64) AppendInt64((Int64)obj); else if (obj is SByte) AppendSByte((SByte)obj); else if (obj is UInt32) AppendUInt32((UInt32)obj); else if (obj is UInt64) AppendUInt64((UInt64)obj); else if (_LoopObject.ContainsKey(obj) == false) { _LoopObject.Add(obj, null); if (obj is IDictionary) AppendJson((IDictionary)obj); else if (obj is IEnumerable) AppendArray((IEnumerable)obj);
else if (obj is DataSet) AppendDataSet((DataSet)obj);
else if (obj is DataTable) AppendDataTable((DataTable)obj);
else if (obj is DataView) AppendDataView((DataView)obj); else AppendOther(obj); _LoopObject.Remove(obj); } else { Buff.Append("undefined"); } }
这2个方法都比较好理解,
一个是用来处理Key的,这里预留了一个参数escape,是为了性能的一些考虑,比如反射时的属性名称,这个是绝对不可能出现一些特殊符号的,所以可以直接作为Json的Key使用
另一个方法是用来作为泛对象(不是泛型对象)的入口方法,所有对象通过这个方法都可以找到对应的处理方法
还有一个对象_LoopObject,这个对象是为了解决循环引用的问题的,比如常用的对象Page(当然没有人会把这个对象转为Json,这里只是用来做说明)中就有一个Page的属性,指向this,如果没有额外的处理,解析将会进入一个循环递归,直到栈溢出(目前已知的几个第三方组件对这个情况的支持都不好,都会抛出异常,这个以后的文章会详细说明),而我这里做的处理是将这种无法解析循环引用对象都返回undefined,也正好可以区别于空对象的null
完整代码

using System; using System.Collections; using System.Collections.Generic; using System.Data; using System.Text; namespace blqw { /// <summary> 用于将C#转换为Json字符串 /// </summary> public class JsonBuilder { private Dictionary<object, object> _LoopObject = new Dictionary<object, object>();//循环引用对象缓存区 protected StringBuilder Buff = new StringBuilder(4096);//字符缓冲区 public string ToJsonString(object obj) { Buff.Length = 0; AppendObject(obj); return Buff.ToString(); } //泛对象 protected void AppendObject(object obj) { if (obj == null) Buff.Append("null"); else if (obj is String) AppendString((String)obj); else if (obj is Int32) AppendInt32((Int32)obj); else if (obj is Boolean) AppendBoolean((Boolean)obj); else if (obj is DateTime) AppendDateTime((DateTime)obj); else if (obj is Double) AppendDouble((Double)obj); else if (obj is Enum) AppendEnum((Enum)obj); else if (obj is Decimal) AppendDecimal((Decimal)obj); else if (obj is Char) AppendChar((Char)obj); else if (obj is Single) AppendSingle((Single)obj); else if (obj is Guid) AppendGuid((Guid)obj); else if (obj is Byte) AppendByte((Byte)obj); else if (obj is Int16) AppendInt16((Int16)obj); else if (obj is Int64) AppendInt64((Int64)obj); else if (obj is SByte) AppendSByte((SByte)obj); else if (obj is UInt32) AppendUInt32((UInt32)obj); else if (obj is UInt64) AppendUInt64((UInt64)obj); else if (_LoopObject.ContainsKey(obj) == false) { _LoopObject.Add(obj, null); if (obj is IDictionary) AppendJson((IDictionary)obj); else if (obj is IEnumerable) AppendArray((IEnumerable)obj); else if (obj is DataSet) AppendDataSet((DataSet)obj); else if (obj is DataTable) AppendDataTable((DataTable)obj); else if (obj is DataView) AppendDataView((DataView)obj); else AppendOther(obj); _LoopObject.Remove(obj); } else { Buff.Append("undefined"); } } protected virtual void AppendOther(object obj) { Type t = obj.GetType(); Buff.Append('{'); string fix = ""; foreach (var p in t.GetProperties()) { if (p.CanRead) { Buff.Append(fix); AppendKey(p.Name, false); object value = p.GetValue(obj, null); AppendObject(value); fix = ","; } } Buff.Append('}'); } /// <summary> " /// </summary> public const char Quot = '"'; /// <summary> : /// </summary> public const char Colon = ':'; /// <summary> , /// </summary> public const char Comma = ','; /// <summary> 追加Key /// </summary> /// <param name="key"></param> /// <param name="escape">key中是否有(引号,回车,制表符等)特殊字符,需要转义</param> protected virtual void AppendKey(string key, bool escape) { if (escape) { AppendString(key); } else { Buff.Append(Quot); Buff.Append(key); Buff.Append(Quot); } Buff.Append(Colon); } //基本类型转换Json字符串写入Buff protected virtual void AppendByte(Byte value) { AppendNumber(value); } protected virtual void AppendDecimal(Decimal value) { AppendNumber(value); } protected virtual void AppendInt16(Int16 value) { AppendNumber(value); } protected virtual void AppendInt32(Int32 value) { AppendNumber(value); } protected virtual void AppendInt64(Int64 value) { AppendNumber(value); } protected virtual void AppendSByte(SByte value) { AppendNumber(value); } protected virtual void AppendUInt16(UInt16 value) { AppendNumber(value); } protected virtual void AppendUInt32(UInt32 value) { AppendNumber(value); } protected virtual void AppendUInt64(UInt64 value) { AppendNumber(value); } protected virtual void AppendDouble(Double value) { AppendNumber(value); } protected virtual void AppendSingle(Single value) { AppendNumber(value); } protected virtual void AppendBoolean(Boolean value) { Buff.Append(value ? "true" : "false"); } protected virtual void AppendChar(Char value) { Buff.Append(Quot); switch (value) { case '\\': case '\n': case '\r': case '\t': case '"': Buff.Append('\\'); break; } Buff.Append(value); Buff.Append(Quot); } protected virtual void AppendString(String value) { Buff.Append(Quot); for (int j = 0; j < value.Length; j++) { switch (value[j]) { case '\\': case '\n': case '\r': case '\t': case '"': Buff.Append('\\'); break; } Buff.Append(value[j]); } Buff.Append(Quot); } protected virtual void AppendDateTime(DateTime value) { Buff.Append(Quot); if (value.Year < 1000) { if (value.Year < 100) { if (value.Year < 10) { Buff.Append("000"); } else { Buff.Append("00"); } } else { Buff.Append("0"); } } Buff.Append(value.Year) .Append('-'); if (value.Month < 10) { Buff.Append('0'); } Buff.Append(value.Month).Append('-'); if (value.Day < 10) { Buff.Append('0'); } Buff.Append(value.Day).Append(' '); if (value.Hour < 10) { Buff.Append('0'); } Buff.Append(value.Hour).Append(Colon); if (value.Minute < 10) { Buff.Append('0'); } Buff.Append(value.Minute).Append(Colon); if (value.Second < 10) { Buff.Append('0'); } Buff.Append(value.Second).Append(Quot); } protected virtual void AppendGuid(Guid value) { Buff.Append(Quot).Append(value.ToString()).Append(Quot); } //枚举 protected virtual void AppendEnum(Enum value) { Buff.Append(Quot).Append(value.ToString()).Append(Quot); } protected virtual void AppendNumber(IConvertible number) { Buff.Append(number.ToString(System.Globalization.NumberFormatInfo.InvariantInfo)); } //转换数组对象 protected virtual void AppendArray(IEnumerable array) { Buff.Append('['); var ee = array.GetEnumerator(); if (ee.MoveNext()) { AppendObject(ee.Current); while (ee.MoveNext()) { Buff.Append(Comma); AppendObject(ee.Current); } } Buff.Append(']'); } //转换键值对对象 protected virtual void AppendJson(IDictionary dict) { AppendJson(dict.Keys, dict.Values); } //分别有键值枚举的对象 protected virtual void AppendJson(IEnumerable keys, IEnumerable values) { Buff.Append('{'); var ke = keys.GetEnumerator(); var ve = values.GetEnumerator(); if (ke.MoveNext() && ve.MoveNext()) { AppendKey(ke.Current + "", true); AppendObject(ve.Current); while (ke.MoveNext() && ve.MoveNext()) { Buff.Append(Comma); AppendKey(ke.Current + "", true); AppendObject(ve.Current); } } Buff.Append('}'); } protected virtual void AppendArray(IEnumerable enumer, Converter<object, object> getVal) { Buff.Append('['); var ee = enumer.GetEnumerator(); if (ee.MoveNext()) { AppendObject(ee.Current); while (ee.MoveNext()) { Buff.Append(Comma); AppendObject(getVal(ee.Current)); } } Buff.Append(']'); } protected virtual void AppendJson(IEnumerable enumer, Converter<object, string> getKey, Converter<object, object> getVal, bool escapekey) { Buff.Append('{'); var ee = enumer.GetEnumerator(); if (ee.MoveNext()) { AppendKey(getKey(ee.Current), escapekey); AppendObject(getVal(ee.Current)); while (ee.MoveNext()) { Buff.Append(Comma); AppendKey(getKey(ee.Current), true); AppendObject(getVal(ee.Current)); } } Buff.Append('}'); } protected virtual void AppendDataSet(DataSet dataset) { Buff.Append('{'); var ee = dataset.Tables.GetEnumerator(); if (ee.MoveNext()) { DataTable table = (DataTable)ee.Current; AppendKey(table.TableName, true); AppendDataTable(table); while (ee.MoveNext()) { Buff.Append(Comma); table = (DataTable)ee.Current; AppendKey(table.TableName, true); AppendDataTable(table); } } Buff.Append('}'); } protected virtual void AppendDataTable(DataTable table) { Buff.Append("{\"columns\":"); AppendArray(table.Columns, o => ((DataColumn)o).ColumnName); Buff.Append(",\"rows\":"); AppendArray(table.Rows, o => ((DataRow)o).ItemArray); Buff.Append('}'); } protected virtual void AppendDataView(DataView tableView) { Buff.Append("{\"columns\":"); AppendArray(tableView.Table.Columns, o => ((DataColumn)o).ColumnName); Buff.Append(",\"rows\":"); AppendArray(tableView, o => ((DataRowView)o).Row.ItemArray); Buff.Append('}'); } } }
完整代码中有部分代码做了调整
调用部分
测试下json格式
灵活扩展
例如 上面的栗子中,枚举转出的是他的字符串形式
如果现在我需要把所有枚举转为对应的数字怎么做呢?
public class EnumValueJsonBuilder : JsonBuilder { protected override void AppendEnum(Enum value) { Buff.Append(value.GetHashCode()); } }
新建一个类,然后重载AppendEnum就可以了
真是超级简单的啦~~~
性能优化
JsonBuilder第一版对一般对象的是进行实时反射的,所以性能不会很好,所以我首先想到的是优化他的性能
看我前几天发表过一篇《[源码]Literacy 快速反射读写对象属性,字段》的文章,这东西的效率不错,用来代替反射正好。
我把优化后的类取名QuickJsonBuilder
在继承JsonBuilder的基础上,我仅仅需要重写一个方法
public class QuickJsonBuilder : JsonBuilder { protected override void AppendOther(object obj) { Literacy lit = new Literacy(obj.GetType()); string fix = ""; Buff.Append('{'); foreach (var p in lit.Property) { Buff.Append(fix); AppendKey(p.Name, false); AppendObject(p.GetValue(obj));
fix = ','; } Buff.Append('}'); } }
像这样我很简单的将原来的实时反射改成了Literacy
但是这样显然并不能保证性能,所以我还要加一个缓存
static Dictionary<Type, Literacy> _LitCache = new Dictionary<Type, Literacy>(); protected override void AppendOther(object obj) { Literacy lit; Type type = obj.GetType(); if (_LitCache.TryGetValue(type, out lit) == false) { lock (_LitCache) { if (_LitCache.TryGetValue(type, out lit) == false) { lit = new Literacy(type); _LitCache.Add(type, lit); } } } string fix = ""; Buff.Append('{'); foreach (var p in lit.Property) { Buff.Append(fix); AppendKey(p.Name, false); AppendObject(p.GetValue(obj));
fix = ','; } Buff.Append('}'); }
缓存本身是全局静态的,所以为了防止多线程并发的问题,特别加了锁
再为了它能性能能够好上那么一点点(更多的是为了自己的强迫症吧....),再修改一些地方
static Dictionary<Type, Literacy> _LitCache = new Dictionary<Type, Literacy>(); protected override void AppendOther(object obj) { Literacy lit; Type type = obj.GetType(); if (_LitCache.TryGetValue(type, out lit) == false) { lock (_LitCache) { if (_LitCache.TryGetValue(type, out lit) == false) { lit = new Literacy(type); _LitCache.Add(type, lit); } } } Buff.Append('{'); var ee = lit.Property.GetEnumerator(); if (ee.MoveNext()) { AppendKey(ee.Current.Name, false); AppendObject(ee.Current.GetValue(obj)); while (ee.MoveNext()) { Buff.Append(','); AppendKey(ee.Current.Name, false); AppendObject(ee.Current.GetValue(obj)); } } Buff.Append('}'); }
好了来看看他和他父亲在性能上的差异:
测试代码和《几个常用Json组件的性能测试》中的是一样的,

I7-2630 4核8线程 主频2.0 内存 1600 4G*2 没有显卡 硬盘 闪迪 msata 128 r:500m/s w:350m/s
不过今天在自己的电脑上运行了,感觉和昨天的测试结果的差异还是有些大的,所以还是那句话:测试结果因配置不同会有所出入,此结果仅供参考
功能扩展
相比较性能优化来说 这个要更重要一些,随着现在硬件各种不值钱,性能上的问题绝对可以在硬件上解决,而功能不足却是大多数时候我们放弃一个现成类库的原因
在这个时候一个类库是否可扩展就显得尤为重要了。
先来看一个栗子
将DataSet转为Json对象
随便来个简单个DataSet

DataTable dt1 = new DataTable("User"); dt1.Columns.Add("UID", typeof(Guid)); dt1.Columns.Add("Name", typeof(string)); dt1.Columns.Add("Birthday", typeof(DateTime)); dt1.Columns.Add("Sex", typeof(int)); dt1.Columns.Add("IsDeleted", typeof(bool)); dt1.Rows.Add(Guid.NewGuid(), "blqw", DateTime.Parse("1986-10-29"), 1, false); dt1.Rows.Add(Guid.NewGuid(), "小明", DateTime.Parse("1990-1-1"), 1, false); dt1.Rows.Add(Guid.NewGuid(), "小华", DateTime.Parse("1990-2-2"), 0, false); DataTable dt2 = new DataTable("UserInfo"); dt2.Columns.Add("ID", typeof(int)); dt2.Columns.Add("UID", typeof(Guid)); dt2.Columns.Add("Address", typeof(string)); dt2.Columns.Add("ZipCode", typeof(int)); dt2.Rows.Add(1, dt1.Rows[0][0], "广州", 100000); dt2.Rows.Add(2, dt1.Rows[1][0], "上海", 200000); dt2.Rows.Add(3, dt1.Rows[2][0], "背景", 300000); DataSet ds = new DataSet(); ds.Tables.Add(dt1); ds.Tables.Add(dt2);
再来看看每个组件的转换结果

//JsonBuilder var a = {"User":{"columns":["UID","Name","Birthday","Sex","IsDeleted"],"rows":[["58cc6573-cb34-4d82-9343-166a3d23f518","blqw","1986-10-29 00:00:00",1,false],["c1ce 27aa-383c-4a90-b180-4f8827ef64a9","小明","1990-01-01 00:00:00",1,false],["79d24a02-7d90-4ade-80c5-93b57314f55b","小华","1990-02-02 00:00:00",0,false]]},"UserInf o":{"columns":["ID","UID","Address","ZipCode"],"rows":[[1,"58cc6573-cb34-4d82-9343-166a3d23f518","广州",100000],[2,"c1ce27aa-383c-4a90-b180-4f8827ef64a9","上海" ,200000],[3,"79d24a02-7d90-4ade-80c5-93b57314f55b","背景",300000]]}} //============================================== //QuickJsonBuilder var a = {"User":{"columns":["UID","Name","Birthday","Sex","IsDeleted"],"rows":[["58cc6573-cb34-4d82-9343-166a3d23f518","blqw","1986-10-29 00:00:00",1,false],["c1ce 27aa-383c-4a90-b180-4f8827ef64a9","小明","1990-01-01 00:00:00",1,false],["79d24a02-7d90-4ade-80c5-93b57314f55b","小华","1990-02-02 00:00:00",0,false]]},"UserInf o":{"columns":["ID","UID","Address","ZipCode"],"rows":[[1,"58cc6573-cb34-4d82-9343-166a3d23f518","广州",100000],[2,"c1ce27aa-383c-4a90-b180-4f8827ef64a9","上海" ,200000],[3,"79d24a02-7d90-4ade-80c5-93b57314f55b","背景",300000]]}} //============================================== //fastJSON.NET var a = {"User":[["f1a4f526-e723-4fb4-930e-0b5250ee3309","blqw","1986-10-29 00:00:00",1,false],["32b289a2-f116-47c3-99cc-0a7804706490","小明","1990-01-01 00:00:00" ,1,false],["a2ca668f-151e-4863-8979-d5a5e7c83a35","小华","1990-02-02 00:00:00",0,false]],"UserInfo":[[1,"f1a4f526-e723-4fb4-930e-0b5250ee3309","广州",100000],[2 ,"32b289a2-f116-47c3-99cc-0a7804706490","上海",200000],[3,"a2ca668f-151e-4863-8979-d5a5e7c83a35","背景",300000]]} ============================================== //Jayrock.Json var a = {"User":[{"UID":"58cc6573-cb34-4d82-9343-166a3d23f518","Name":"blqw","Birthday":"1986-10-29T00:00:00.0000000+08:00","Sex":1,"IsDeleted":false},{"UID":"c1ce 27aa-383c-4a90-b180-4f8827ef64a9","Name":"小明","Birthday":"1990-01-01T00:00:00.0000000+08:00","Sex":1,"IsDeleted":false},{"UID":"79d24a02-7d90-4ade-80c5-93b573 14f55b","Name":"小华","Birthday":"1990-02-02T00:00:00.0000000+08:00","Sex":0,"IsDeleted":false}],"UserInfo":[{"ID":1,"UID":"58cc6573-cb34-4d82-9343-166a3d23f518 ","Address":"广州","ZipCode":100000},{"ID":2,"UID":"c1ce27aa-383c-4a90-b180-4f8827ef64a9","Address":"上海","ZipCode":200000},{"ID":3,"UID":"79d24a02-7d90-4ade-8 0c5-93b57314f55b","Address":"背景","ZipCode":300000}]} //============================================== //Newtonsoft.Json var a = {"User":[{"UID":"58cc6573-cb34-4d82-9343-166a3d23f518","Name":"blqw","Birthday":"\/Date(530899200000+0800)\/","Sex":1,"IsDeleted":false},{"UID":"c1ce27aa-3 83c-4a90-b180-4f8827ef64a9","Name":"小明","Birthday":"\/Date(631123200000+0800)\/","Sex":1,"IsDeleted":false},{"UID":"79d24a02-7d90-4ade-80c5-93b57314f55b","Nam e":"小华","Birthday":"\/Date(633888000000+0800)\/","Sex":0,"IsDeleted":false}],"UserInfo":[{"ID":1,"UID":"58cc6573-cb34-4d82-9343-166a3d23f518","Address":"广州" ,"ZipCode":100000},{"ID":2,"UID":"c1ce27aa-383c-4a90-b180-4f8827ef64a9","Address":"上海","ZipCode":200000},{"ID":3,"UID":"79d24a02-7d90-4ade-80c5-93b57314f55b", "Address":"背景","ZipCode":300000}]} //============================================== //ServiceStack.Text 报错 //TestJavaScriptSerializer 报错
我们可以很清楚的发现每个组件都有各自的转换方式,结果都不相同
- fastJSON.NET 结果中每列的"列名"丢失了
- Jayrock.Json 算是比较标准的格式了
- Newtonsoft.Json 数据格式和Jayrock.Json类似,但是时间格式太蛋疼了,造成的结果就是这个字符串直接在网页js中运行是不可能的,需要2次转换
- TestJavaScriptSerializer 报错,无法转换
- TestServiceStack 报错,无法转换
- JsonBuilder,因为是我自己写的,所以有自己的格式,我习惯将Table转换为2个部分一个columns,一个rows,因为在行数多的情况下,列名每次都重复会增加数据大小
以上几个组件的结果 可能可以通过参数设置,我不确定 但即使是参数设定,也只不过是选择几种预置的格式罢了。
这时候JsonBuilder灵活性就显现出来了
假设我现在的项目有以下需求:
- DataTable转换需要可以在,Jayrock.Json和JsonBuilder格式之间切换
- DateTime可以在 Data,Time,DataTime 3种模式中切换
- Guid要去掉中间的连字符(-)
- true,false要转换为1,0
OK 现在我需要重写4个方法 AppendDataTable(如果有必要也一起重写AppendDataView),AppendDateTime,AppendGuid,AppendBoolean

using System; using System.Collections.Generic; using System.Data; using System.Linq; using System.Text; namespace blqw { class MyJsonBuilder : QuickJsonBuilder { /// <summary> 表风格 /// <para> 默认 { columns:[colname,colname2],rows:[[x,x],[y,y]] } </para> /// <para> 1 [{ colname:x,colname2:x },{ colname:y,colname2:y }] </para> /// </summary> public int TableStyle { get; set; } /// <summary> 时间风格 /// <para> 默认 yyyy-MM-dd HH:mm:ss </para> /// <para> 1 yyyy-MM-dd </para> /// <para> 2 HH:mm:ss </para> /// </summary> public int DateTimeStyle { get; set; } protected override void AppendDateTime(DateTime value) { switch (DateTimeStyle) { case 1: Buff.Append('"'); Buff.Append(value.ToString("yyyy-MM-dd")); Buff.Append('"'); break; case 2: Buff.Append('"'); Buff.Append(value.ToString("HH:mm:ss")); Buff.Append('"'); break; default: base.AppendDateTime(value); break; } } protected override void AppendGuid(Guid value) { Buff.Append('"'); Buff.Append(value.ToString("N")); Buff.Append('"'); } protected override void AppendBoolean(bool value) { base.AppendNumber(value.GetHashCode()); } protected override void AppendDataTable(System.Data.DataTable table) { if (TableStyle == 1) { Buff.Append('['); var fix = ""; foreach (DataRow row in table.Rows) { Buff.Append(fix); var fix2 = ""; Buff.Append('{'); foreach (DataColumn col in table.Columns) { Buff.Append(fix2); AppendKey(col.ColumnName,false); AppendObject(row[col]); fix2 = ","; } Buff.Append('}'); fix = ","; } Buff.Append(']'); } else { base.AppendDataTable(table); } } } }
然后就可以调用了
MyJsonBuilder jb = new MyJsonBuilder(); Console.WriteLine("TableStyle=1 ; DateTimeStyle=1"); jb.TableStyle = 1; jb.DateTimeStyle = 1; Console.WriteLine(jb.ToJsonString(ds)); Console.WriteLine(""); Console.WriteLine("TableStyle=0 ; DateTimeStyle=2"); jb.TableStyle = 0; jb.DateTimeStyle = 2; Console.WriteLine(jb.ToJsonString(ds));
结果
TableStyle=1 ; DateTimeStyle=1 {"User":[{"UID":"0f3f70f0939546d8803d8cd0ef4de8ec","Name":"blqw","Birthday":"1986-10-29","Sex":1,"IsDeleted":0},{"UID":"72489efcbc694801a358b697414892ee","Name":"小明","Birthday":"1990-01-01","Sex":1,"IsDeleted":0},{"UID":"865cddbb1f0c427e97a724844c35a83d","Name":"小华","Birthday":"1990-02-02","Sex":0,"IsDeleted":0}],"UserInfo":[{"ID":1,"UID":"0f3f70f0939546d8803d8cd0ef4de8ec","Address":"广州","ZipCode":100000},{"ID":2,"UID":"72489efcbc694801a358b697414892ee","Address":"上海","ZipCode":200000},{"ID":3,"UID":"865cddbb1f0c427e97a724844c35a83d","Address":"背景","ZipCode":300000}]} TableStyle=0 ; DateTimeStyle=2 {"User":{"columns":["UID","Name","Birthday","Sex","IsDeleted"],"rows":[["0f3f70f0939546d8803d8cd0ef4de8ec","blqw","00:00:00",1,0],["72489efcbc694801a358b697414892ee","小明","00:00:00",1,0],["865cddbb1f0c427e97a724844c35a83d","小华","00:00:00",0,0]]},"UserInfo":{"columns":["ID","UID","Address","ZipCode"],"rows":[[1,"0f3f70f0939546d8803d8cd0ef4de8ec","广州",100000],[2,"72489efcbc694801a358b697414892ee","上海",200000],[3,"865cddbb1f0c427e97a724844c35a83d","背景",300000]]}} 请按任意键继续. . .