Newtonsoft.Json / System.Text.Json 功能強大,但若專案僅需簡單的物件↔JSON 轉換,引入大型套件屬於「殺雞用牛刀」。本文提供一個 零第三方依賴、單一檔案、約 300 行 的 ConvertJson 靜態類,涵蓋 List、單一物件、DataSet、DataTable、DataReader 等常見資料源,適合輕量級專案或受限環境直接複製使用。
| 方法 | 輸入 | 輸出 | 典型場景 |
|---|---|---|---|
ListToJson<T>(IList<T>) | 泛型集合 | JSON 字串 | 實體清單序列化 |
ToJson(object) | 單一物件 | JSON 字串 | DTO 序列化 |
ToJson(IEnumerable) | 非泛型集合 | JSON 陣列 | 舊版 ArrayList 等 |
ToJson(DataSet) | DataSet | 巢狀 JSON | 多表資料集 |
ToJson(DataTable) | DataTable | JSON 陣列 | 單表查詢結果 |
ToJson(DbDataReader) | DataReader | JSON 陣列 | 高效流式讀取 |
private static string EscapeJson(string s)
{
var sb = new StringBuilder();
foreach (char c in s)
{
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: sb.Append(c); break;
}
}
return sb.ToString();
}
private static string FormatValue(string raw, Type type)
{
if (type == typeof(string))
return $"\"{EscapeJson(raw)}\"";
if (type == typeof(DateTime) || type == typeof(Guid) || type == typeof(TimeSpan))
return $"\"{raw}\"";
if (type == typeof(bool))
return raw.ToLower();
if (string.IsNullOrEmpty(raw) && type != typeof(string))
return "\"\""; // 非字串型別的 null/空值輸出空字串
return raw; // 數值型別直接輸出
}
public static string ListToJson<T>(IList<T> list, string jsonName = null)
{
if (list == null || list.Count == 0) return $"{{\"{(jsonName ?? typeof(T).Name)}\":[]}}";
var sb = new StringBuilder();
string rootName = jsonName ?? list[0].GetType().Name;
sb.Append($"{{\"{rootName}\":[");
var props = typeof(T).GetProperties();
for (int i = 0; i < list.Count; i++)
{
sb.Append("{");
for (int j = 0; j < props.Length; j++)
{
var val = props[j].GetValue(list[i]);
string strVal = val?.ToString() ?? "";
sb.Append($"\"{props[j].Name}\":{FormatValue(strVal, props[j].PropertyType)}");
if (j < props.Length - 1) sb.Append(",");
}
sb.Append("}");
if (i < list.Count - 1) sb.Append(",");
}
sb.Append("]}");
return sb.ToString();
}
public static string ToJson(DataTable dt, string jsonName = null)
{
var sb = new StringBuilder();
string name = jsonName ?? dt.TableName;
sb.Append($"{{\"{name}\":[");
foreach (DataRow row in dt.Rows)
{
sb.Append("{");
for (int c = 0; c < dt.Columns.Count; c++)
{
var col = dt.Columns[c];
string val = row[c]?.ToString() ?? "";
sb.Append($"\"{col.ColumnName}\":{FormatValue(val, col.DataType)}");
if (c < dt.Columns.Count - 1) sb.Append(",");
}
sb.Append("},");
}
if (dt.Rows.Count > 0) sb.Length--; // 移除最後逗號
sb.Append("]}");
return sb.ToString();
}
// 1. List 序列化
var users = new List { new User { Id = 1, Name = "張三" } };
string json = ConvertJson.ListToJson(users);
// {"User":[{"Id":1,"Name":"張三"}]}
// 2. DataTable 序列化
DataTable dt = GetDataTableFromDb();
json = ConvertJson.ToJson(dt, "Users");
// 3. DataReader 串流轉換(不載入全表到記憶體)
using (var reader = cmd.ExecuteReader())
json = ConvertJson.ToJson(reader);
FormatValue 遞迴處理 IEnumerable 與複雜型別。ToString() 格式,若需 ISO 8601 請在 FormatValue 加入 DateTime.Parse(val).ToString("o")。System.Text.Json 的 Utf8JsonWriter 零配置寫入。System.Text.Json.JsonSerializer.Deserialize 或自行實作解析器。完整 ConvertJson.cs 已整理為單一檔案,可直接拖入專案使用。命名空間建議調整為專案專屬(如 MyProject.Utils),避免衝突。
在無法引入 NuGet 套件、或追求極致輕量的情境下,ConvertJson 提供了「夠用、好改、零依賴」的 JSON 序列化能力,涵蓋 ADO.NET 主流資料容器,是工具箱裡的實用小刀。