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

簡單,是由於只有一個類html

輕量,是由於整個類代碼只有300行json

靈活,是由於擴展方式只須要繼承重寫某個方法便可數組

 

補充:修正沒法處理可空值類型的bug緩存

 

首先我將這個類稱之爲JsonBuilder,我但願它以StringBuilder的方式來實現Json字符串的轉換ide

public class JsonBuilder
{
    protected StringBuilder Buff = new StringBuilder(4096);//字符緩衝區
    public string ToJsonString(object obj)
    {
        .......
        return Buff.ToString();
    }
    .......
}

而後我爲但願爲每個基礎類型單獨完成一個方法,而且方法能夠被重寫oop

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); }

這樣的好處是我能夠在子類中靈活的選擇重寫所有的數字類型,仍是隻重寫某種特定類型ui

而後接着,我須要完成一些已知類型的轉換方法,好比數組,集合,字典,數據表等等this

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('}');
        }
    }
}
JsonBuilder

完整代碼中有部分代碼作了調整

調用部分

測試下json格式

 

靈活擴展

例如 上面的栗子中,枚舉轉出的是他的字符串形式

若是如今我須要把全部枚舉轉爲對應的數字怎麼作呢?

public class EnumValueJsonBuilder : JsonBuilder
{
    protected override void AppendEnum(Enum value)
    {
        Buff.Append(value.GetHashCode());
    }
}

 新建一個類,而後重載AppendEnum就能夠了

真是超級簡單的啦~~~

.

.

.

.

.

不過,若是你覺得這樣就結束了,那麼你就大錯特錯了

如今纔剛剛開始.....未完待續....

相關文章
相關標籤/搜索