在 Web Forms 時代,常需將 SqlDataReader 直接序列化為 JSON 供前端下拉選單、AJAX 回傳使用。雖然現代專案建議用 Entity Framework + JSON.NET (Newtonsoft.Json) / System.Text.Json,但維舊專案仍需此工具。以下提供健壯、可復用的實作。
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.Text;
using Newtonsoft.Json; // 建議引用 JSON.NET,內建序列化更可靠
public static class JsonHelper
{
///
/// 將 SqlDataReader 序列化為 JSON 字串 (使用 JSON.NET)
///
public static string ToJson(SqlDataReader reader, string rootName = "data")
{
if (reader == null) throw new ArgumentNullException(nameof(reader));
var list = new List>();
while (reader.Read())
{
var dict = new Dictionary(reader.FieldCount);
for (int i = 0; i < reader.FieldCount; i++)
{
object val = reader.GetValue(i);
// DBNull 轉 null,DateTime 格式化
dict[reader.GetName(i)] = val == DBNull.Value ? null :
(val is DateTime dt ? dt.ToString("yyyy-MM-dd HH:mm:ss") : val);
}
list.Add(dict);
}
var result = new Dictionary { [rootName] = list };
return JsonConvert.SerializeObject(result, Formatting.None);
}
///
/// 無 JSON.NET 依賴的手工實作 (舊專案相容)
///
public static string ToJsonManual(SqlDataReader reader, string tableName = "Table")
{
if (reader == null) throw new ArgumentNullException(nameof(reader));
var sb = new StringBuilder();
sb.Append('{').Append('"').Append(tableName).Append("\":{");
sb.Append("\"records\":[");
bool firstRow = true;
try
{
while (reader.Read())
{
if (!firstRow) sb.Append(',');
firstRow = false;
sb.Append('{');
bool firstCol = true;
for (int i = 0; i < reader.FieldCount; i++)
{
if (!firstCol) sb.Append(',');
firstCol = false;
string name = reader.GetName(i);
object val = reader.GetValue(i);
string jsonVal = val == DBNull.Value ? "null" :
(val is string || val is DateTime || val is Guid ?
"\"" + EscapeJson(val.ToString()) + "\"" :
val.ToString().ToLower()); // bool/int/decimal 直接輸出
sb.Append('"').Append(EscapeJson(name)).Append("\":").Append(jsonVal);
}
sb.Append('}');
}
}
finally
{
reader.Close(); // 確保關閉 Reader
}
sb.Append("]}");
return sb.ToString();
}
private static string EscapeJson(string s)
{
return s.Replace("\\", "\\\\")
.Replace("\"", "\\\"")
.Replace("\n", "\\n")
.Replace("\r", "\\r")
.Replace("\t", "\\t");
}
}
// Web Forms / ASMX / Generic Handler
public void GetProductsJson()
{
string sql = "SELECT ProductID, ProductName, UnitPrice, Discontinued FROM Products";
using (var conn = new SqlConnection(ConnStr))
using (var cmd = new SqlCommand(sql, conn))
{
conn.Open();
using (var reader = cmd.ExecuteReader(CommandBehavior.CloseConnection))
{
string json = JsonHelper.ToJson(reader, "products");
Response.ContentType = "application/json; charset=utf-8";
Response.Write(json);
}
}
}
// 前端 jQuery 使用
$.getJSON('GetProductsJson.ashx', function(data) {
$.each(data.products, function(i, item) {
$('#ddlProduct').append($('
// 使用 JSON.NET (專案建議安裝 Newtonsoft.Json NuGet)
public string GetProductsJsonModern()
{
var dt = new DataTable();
using (var da = new SqlDataAdapter("SELECT * FROM Products", ConnStr))
da.Fill(dt);
// DataTable 直接序列化
string json = JsonConvert.SerializeObject(dt, Formatting.None);
// 或轉為 List 更精確控制
return json;
}
// 使用 System.Text.Json (.NET Core 3.0+ / .NET 5+)
using System.Text.Json;
public string GetProductsJsonCore()
{
var list = new List
| 方法 | 記憶體 | 速度 | 適用場景 |
|---|---|---|---|
| DataReader + 手工 StringBuilder | 低 (串流) | 快 | 大量資料、無 JSON.NET 環境 |
| DataReader + JSON.NET | 中 | 快 | 一般專案、需日期/Null 處理 |
| DataTable.Fill + 序列化 | 高 (全載入記憶體) | 中 | 小量資料、需 DataTable 功能 |
| Entity Framework + 投影 | 中 | 中 | 現代專案、強型別、LINQ 查詢 |
using 或 finally { reader.Close(); },否則連線池洩漏。yyyy-MM-ddTHH:mm:ss) 或 Unix Timestamp,前端解析最穩定。Response.ContentType = "application/json; charset=utf-8",避免中文亂碼。新專案請直接使用 System.Text.Json 或 Newtonsoft.Json 配合強型別模型,手工拼 JSON 僅限極舊環境維護。