本文參考資料
一段廢話
之前我已經介紹了這個方案的名稱為JsonBuilder,這套方案最大的好處在於它的靈活可擴展性上,所以我可以很方便的對他進行優化和擴展!
性能優化
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]]}} 請按任意鍵繼續. . .
比完整Demo還要完整的Demo下載
ps 此Demo中包含了一些我下一篇文章會寫的內容,如果里面的代碼和文章中的不完全一樣,以Demo中可運行代碼為准