首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> .NET 網頁程式設計 >> ASP.NET 基礎 >> ASP.NET 基礎

ASP.NET 多型別 JSON 序列化工具類完整實作

     編輯:ASP.NET 基礎

前言

在 ASP.NET WebForms / MVC 早期專案中,若未引入 Newtonsoft.Json (Json.NET),常需自行實作 JSON 序列化。本文提供一個涵蓋 List、DataTable、DataSet、DataReader、單一物件、匿名型別、自訂類型 的完整序列化工具類,採用 `StringBuilder` 高效拼接,支援特殊字元轉義與日期/布林型別格式化。

一、核心工具類:ConvertJson

using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Data.Common;
using System.Reflection;
using System.Text;
using System.Text.RegularExpressions;
using System.ComponentModel;
using System.Globalization;

public static class ConvertJson
{
    #region 私有輔助方法

    /// 
    /// JSON 特殊字元轉義 (RFC 8259)
    /// 
    private static string EscapeJsonString(string input)
    {
        if (string.IsNullOrEmpty(input)) return "\"\"";
        
        var sb = new StringBuilder(input.Length + 10);
        foreach (char c in input)
        {
            switch (c)
            {
                case '"': sb.Append("\\\""); break;
                case '\\': sb.Append("\\\\"); break;
                case '/': sb.Append("\\/"); break;
                case '\b': sb.Append("\\b"); break;
                case '\f': sb.Append("\\f"); break;
                case '\n': sb.Append("\\n"); break;
                case '\r': sb.Append("\\r"); break;
                case '\t': sb.Append("\\t"); break;
                default:
                    // 控制字元 (U+0000 ~ U+001F) 轉 \uXXXX
                    if (c < ' ') sb.AppendFormat("\\u{0:X4}", (int)c);
                    else sb.Append(c);
                    break;
            }
        }
        return "\"" + sb.ToString() + "\"";
    }

    /// 
    /// 依型別格式化值
    /// 
    private static string FormatValue(object value, Type type)
    {
        if (value == null || value == DBNull.Value) return "null";
        
        string str = value.ToString();
        
        if (type == typeof(string) || type == typeof(char))
            return EscapeJsonString(str);
        
        if (type == typeof(DateTime))
            return EscapeJsonString(Convert.ToDateTime(value).ToString("yyyy-MM-ddTHH:mm:ss"));
        
        if (type == typeof(DateTimeOffset))
            return EscapeJsonString(Convert.ToDateTimeOffset(value).ToString("yyyy-MM-ddTHH:mm:sszzz"));
        
        if (type == typeof(bool))
            return str.ToLower(); // true/false
        
        if (type == typeof(Guid))
            return EscapeJsonString(str);
        
        // 數值型別直接輸出
        if (type.IsPrimitive || type == typeof(decimal) || type == typeof(double) || type == typeof(float))
            return str;
        
        // 其餘複雜型別嘗試遞迴序列化
        return SerializeObject(value);
    }

    #endregion

    #region 公開序列化方法

    /// List<T> 序列化
    public static string ListToJson<T>(IList<T> list, string jsonName = null)
    {
        if (list == null || list.Count == 0) return "{\"" + (jsonName ?? "list") + "\":[]}";
        
        var sb = new StringBuilder();
        string name = jsonName ?? typeof(T).Name;
        sb.Append("{\"" + name + "\":[");
        
        for (int i = 0; i < list.Count; i++)
        {
            sb.Append(SerializeObject(list[i]));
            if (i < list.Count - 1) sb.Append(",");
        }
        sb.Append("]}");
        return sb.ToString();
    }

    /// 單一物件序列化 (含匿名型別)
    public static string ToJson(object obj)
    {
        if (obj == null) return "null";
        return SerializeObject(obj);
    }

    /// IEnumerable 序列化 (陣列、集合)
    public static string ToJson(IEnumerable enumerable)
    {
        if (enumerable == null) return "[]";
        
        var sb = new StringBuilder("[");
        bool first = true;
        foreach (var item in enumerable)
        {
            if (!first) sb.Append(",");
            sb.Append(SerializeObject(item));
            first = false;
        }
        sb.Append("]");
        return sb.ToString();
    }

    /// DataSet 序列化
    public static string ToJson(DataSet dataSet)
    {
        if (dataSet == null) return "{}";
        
        var sb = new StringBuilder("{");
        bool firstTable = true;
        foreach (DataTable table in dataSet.Tables)
        {
            if (!firstTable) sb.Append(",");
            sb.Append("\"" + table.TableName + "\":" + ToJson(table));
            firstTable = false;
        }
        sb.Append("}");
        return sb.ToString();
    }

    /// DataTable 序列化
    public static string ToJson(DataTable dt, string jsonName = null)
    {
        if (dt == null) return "{\"" + (jsonName ?? "table") + "\":[]}";
        
        var sb = new StringBuilder();
        string name = jsonName ?? dt.TableName;
        sb.Append("{\"" + name + "\":[");
        
        for (int i = 0; i < dt.Rows.Count; i++)
        {
            sb.Append("{");
            for (int j = 0; j < dt.Columns.Count; j++)
            {
                var col = dt.Columns[j];
                var val = dt.Rows[i][j];
                sb.Append("\"" + col.ColumnName + "\":" + FormatValue(val, col.DataType));
                if (j < dt.Columns.Count - 1) sb.Append(",");
            }
            sb.Append("}");
            if (i < dt.Rows.Count - 1) sb.Append(",");
        }
        sb.Append("]}");
        return sb.ToString();
    }

    /// DataReader 序列化 (逐列流式,記憶體友善)
    public static string ToJson(DbDataReader reader)
    {
        if (reader == null) return "[]";
        
        var sb = new StringBuilder("[");
        bool firstRow = true;
        
        while (reader.Read())
        {
            if (!firstRow) sb.Append(",");
            sb.Append("{");
            
            for (int i = 0; i < reader.FieldCount; i++)
            {
                string name = reader.GetName(i);
                Type type = reader.GetFieldType(i);
                object val = reader[i];
                
                sb.Append("\"" + name + "\":" + FormatValue(val, type));
                if (i < reader.FieldCount - 1) sb.Append(",");
            }
            sb.Append("}");
            firstRow = false;
        }
        
        reader.Close(); // 關閉 Reader
        sb.Append("]");
        return sb.ToString();
    }

    #endregion

    #region 核心遞迴序列化邏輯

    private static string SerializeObject(object obj)
    {
        if (obj == null) return "null";
        
        Type type = obj.GetType();
        
        // 基本型別與字串
        if (type.IsPrimitive || type == typeof(string) || type == typeof(decimal) || 
            type == typeof(DateTime) || type == typeof(DateTimeOffset) || type == typeof(Guid) ||
            type == typeof(TimeSpan))
        {
            return FormatValue(obj, type);
        }
        
        // 列舉
        if (type.IsEnum)
            return FormatValue(Convert.ToInt32(obj), typeof(int));
        
        // 陣列/集合
        if (obj is IEnumerable enumerable && !(obj is string))
            return ToJson(enumerable);
        
        // 一般物件 (含匿名型別) - 反射屬性
        var props = type.GetProperties(BindingFlags.Public | BindingFlags.Instance);
        var sb = new StringBuilder("{");
        bool first = true;
        
        foreach (var prop in props)
        {
            if (!prop.CanRead) continue;
            if (prop.GetIndexParameters().Length > 0) continue; // 跳過索引器
            
            object value = prop.GetValue(obj, null);
            if (!first) sb.Append(",");
            
            sb.Append("\"" + prop.Name + "\":" + FormatValue(value, prop.PropertyType));
            first = false;
        }
        sb.Append("}");
        return sb.ToString();
    }

    #endregion
}

二、使用範例

2.1 List<T> 序列化

var users = new List<User> {
    new User { Id = 1, Name = "張三", Birth = new DateTime(1990,1,1), Active = true },
    new User { Id = 2, Name = "李四", Birth = new DateTime(1995,5,5), Active = false }
};
string json = ConvertJson.ListToJson(users, "users");
// {"users":[{"Id":1,"Name":"張三","Birth":"1990-01-01T00:00:00","Active":true},...]}

2.2 DataTable 序列化

DataTable dt = GetDataTable(); // 從資料庫填充
string json = ConvertJson.ToJson(dt, "products");

2.3 DataReader 串流序列化 (大量資料)

using (var conn = new SqlConnection(cs))
using (var cmd = new SqlCommand("SELECT * FROM LargeTable", conn))
{
    conn.Open();
    using (var reader = cmd.ExecuteReader())
    {
        // 直接輸出至 Response,避免記憶體累積
        Response.ContentType = "application/json";
        Response.Write(ConvertJson.ToJson(reader));
    }
}

2.4 匿名型別 / 動態物件

var data = new { 
    Code = 200, 
    Msg = "Success", 
    Data = new { UserId = 100, Token = "abc123" },
    Tags = new[] { "tag1", "tag2" }
};
string json = ConvertJson.ToJson(data);
// {"Code":200,"Msg":"Success","Data":{"UserId":100,"Token":"abc123"},"Tags":["tag1","tag2"]}

三、擴展:自訂類型轉換器支援

若需序列化非標準型別 (如 `CustomData`),可實作 `ICustomJsonSerializable` 介面:

public interface ICustomJsonSerializable
{
    string ToJson();
}

public class CustomData : ICustomJsonSerializable
{
    public string Value { get; set; }
    public string ToJson() => "\"" + EscapeJsonString(Value) + "\"";
}

// 在 SerializeObject 中加入判斷
if (obj is ICustomJsonSerializable custom)
    return custom.ToJson();

四、效能與現代化建議

場景建議
新專案 / .NET 4.5+直接使用 Newtonsoft.Json (Json.NET) 或 System.Text.Json (.NET Core 3.0+),效能、功能、維護性全面優於手寫實作
舊專案維護 (.NET 2.0/3.5)本工具類可直接使用,無外部依賴
極高效能需求使用 `System.Text.Json` (零分配、SIMD 加速) 或 `Utf8Json`
DataReader 大量資料本工具 `ToJson(DbDataReader)` 採流式寫入,記憶體佔用恆定,優於先 Load DataTable

五、常見問題