怎樣才能真正的靈活,高擴充性?這裡告訴你!
本文參考資料
一種簡單,輕量,靈活的C#對象轉Json對象的方案
[源碼]Literacy 快速反射讀寫對象屬性,字段
一段廢話
之前我已經介紹了這個方案的名稱為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元件的性能測試》中的是一樣的,
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsIiZpdmLrN2bsJEZlR3YhJHdu92Qvw1cy9GdhNWak5WSn5WaulGb0V3TvwVbvNmLzd2bsJmbj5ycldWYtl2Lc9CX6MHc0RHaiojIsJye.gif)
I7-2630 4核8線程 主頻2.0
記憶體 1600 4G*2
沒有顯示卡
硬碟 閃迪 msata 128 r:500m/s w:350m/s
測試機配置
不過今天在自己的電腦上運作了,感覺和昨天的測試結果的差異還是有些大的,是以還是那句話:測試結果因配置不同會有所出入,此結果僅供參考
功能擴充
相比較性能優化來說 這個要更重要一些,随着現在硬體各種不值錢,性能上的問題絕對可以在硬體上解決,而功能不足卻是大多數時候我們放棄一個現成類庫的原因
在這個時候一個類庫是否可擴充就顯得尤為重要了。
先來看一個栗子
将DataSet轉為Json對象
随便來個簡單個DataSet
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsIiZpdmLrN2bsJEZlR3YhJHdu92Qvw1cy9GdhNWak5WSn5WaulGb0V3TvwVbvNmLzd2bsJmbj5ycldWYtl2Lc9CX6MHc0RHaiojIsJye.gif)
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);
C#代碼
再來看看每個元件的轉換結果
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsIiZpdmLrN2bsJEZlR3YhJHdu92Qvw1cy9GdhNWak5WSn5WaulGb0V3TvwVbvNmLzd2bsJmbj5ycldWYtl2Lc9CX6MHc0RHaiojIsJye.gif)
//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
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsIiZpdmLrN2bsJEZlR3YhJHdu92Qvw1cy9GdhNWak5WSn5WaulGb0V3TvwVbvNmLzd2bsJmbj5ycldWYtl2Lc9CX6MHc0RHaiojIsJye.gif)
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);
}
}
}
}
C#代碼
然後就可以調用了
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中可運作代碼為準
我寫的文章,除了純代碼,其他的都是想表達一種思想,一種解決方案.希望各位看官不要局限于文章中的現成的代碼,要多關注整個文章的主題思路,謝謝!
我釋出的代碼,沒有任何版權,遵守WTFPL協定(如有引用,請遵守被引用代碼的協定)
qq群:5946699 希望各位喜愛C#的朋友可以在這裡交流學習,分享程式設計的心得和快樂