天天看點

一種簡單,輕量,靈活的C#對象轉Json對象的方案(續)

怎樣才能真正的靈活,高擴充性?這裡告訴你!

本文參考資料

一種簡單,輕量,靈活的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元件的性能測試》中的是一樣的,

一種簡單,輕量,靈活的C#對象轉Json對象的方案(續)
一種簡單,輕量,靈活的C#對象轉Json對象的方案(續)
I7-2630 4核8線程 主頻2.0

記憶體 1600 4G*2

沒有顯示卡

硬碟 閃迪 msata 128 r:500m/s w:350m/s      

測試機配置

不過今天在自己的電腦上運作了,感覺和昨天的測試結果的差異還是有些大的,是以還是那句話:測試結果因配置不同會有所出入,此結果僅供參考

一種簡單,輕量,靈活的C#對象轉Json對象的方案(續)

  功能擴充

相比較性能優化來說 這個要更重要一些,随着現在硬體各種不值錢,性能上的問題絕對可以在硬體上解決,而功能不足卻是大多數時候我們放棄一個現成類庫的原因

在這個時候一個類庫是否可擴充就顯得尤為重要了。

先來看一個栗子

将DataSet轉為Json對象

随便來個簡單個DataSet

一種簡單,輕量,靈活的C#對象轉Json對象的方案(續)
一種簡單,輕量,靈活的C#對象轉Json對象的方案(續)
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#代碼

一種簡單,輕量,靈活的C#對象轉Json對象的方案(續)

再來看看每個元件的轉換結果

一種簡單,輕量,靈活的C#對象轉Json對象的方案(續)
一種簡單,輕量,靈活的C#對象轉Json對象的方案(續)
//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 報錯      

各元件轉換結果

我們可以很清楚的發現每個元件都有各自的轉換方式,結果都不相同

  1. fastJSON.NET  結果中每列的"列名"丢失了
  2. Jayrock.Json 算是比較标準的格式了
  3. Newtonsoft.Json 資料格式和Jayrock.Json類似,但是時間格式太蛋疼了,造成的結果就是這個字元串直接在網頁js中運作是不可能的,需要2次轉換
  4. TestJavaScriptSerializer 報錯,無法轉換
  5. TestServiceStack 報錯,無法轉換
  6. JsonBuilder,因為是我自己寫的,是以有自己的格式,我習慣将Table轉換為2個部分一個columns,一個rows,因為在行數多的情況下,列名每次都重複會增加資料大小

以上幾個元件的結果 可能可以通過參數設定,我不确定 但即使是參數設定,也隻不過是選擇幾種預置的格式罷了。

這時候JsonBuilder靈活性就顯現出來了

假設我現在的項目有以下需求:

  1. DataTable轉換需要可以在,Jayrock.Json和JsonBuilder格式之間切換
  2. DateTime可以在 Data,Time,DataTime 3種模式中切換
  3. Guid要去掉中間的連字元(-)
  4. true,false要轉換為1,0

OK 現在我需要重寫4個方法 AppendDataTable(如果有必要也一起重寫AppendDataView),AppendDateTime,AppendGuid,AppendBoolean

一種簡單,輕量,靈活的C#對象轉Json對象的方案(續)
一種簡單,輕量,靈活的C#對象轉Json對象的方案(續)
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#的朋友可以在這裡交流學習,分享程式設計的心得和快樂