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

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);
C#代碼

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

//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

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中可運行代碼為准


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM