DataTable序列化前必须将DBNull.Value替换为null或空字符串,否则Newtonsoft.Json和System.Text.Json均会因类型转换失败而抛异常;时间格式需显式配置以匹配前端要求,避免时区与解析错误。

DataTable序列化前必须处理null值
直接用JsonConvert.SerializeObject转DataTable,遇到DBNull.Value会抛JsonSerializationException: Unable to cast object of type 'System.DBNull' to type 'System.IConvertible'。这不是配置问题,是Newtonsoft.Json默认不处理数据库空值。
- 最稳妥的做法:遍历
DataTable,把所有DataRow里的DBNull.Value替换成null或空字符串(根据业务定) - 别依赖
JsonSerializerSettings.NullValueHandling = NullValueHandling.Ignore——它只跳过字段,不解决类型转换失败 - 如果用
System.Text.Json(.NET Core 3.0+),同样报错,且没内置DBNull映射,必须手动预处理
foreach (DataRow row in dt.Rows)
{
for (int i = 0; i < row.ItemArray.Length; i++)
{
if (row[i] == DBNull.Value) row[i] = null;
}
}
用Newtonsoft.Json时要关掉ReferenceLoopHandling
DataTable内部有DataSet、DataTable、DataColumn之间的循环引用(比如column.Table又指回原表),默认开启ReferenceLoopHandling会出错或生成冗余$id/$ref字段,干扰前端解析。
- 显式设置
ReferenceLoopHandling = ReferenceLoopHandling.Ignore - 别用
PreserveReferencesHandling——它只为对象图设计,对表格数据纯属画蛇添足 - 如果后续要反序列化回
DataTable,这个设置不影响,因为反序列化走的是另一套逻辑
var settings = new JsonSerializerSettings
{
ReferenceLoopHandling = ReferenceLoopHandling.Ignore,
NullValueHandling = NullValueHandling.Include // 显式声明,避免歧义
};
string json = JsonConvert.SerializeObject(dt, settings);
System.Text.Json在.NET 5+可以免预处理但有限制
.NET 5起,System.Text.Json支持通过JsonSerializerOptions.Converters注册自定义转换器,能直接处理DBNull。但它不支持DataTable原生序列化——你得自己写JsonConverter<DataTable>,或者退一步,转成List<Dictionary<string, object>>再序列化。
- 推荐路径:
dt.AsEnumerable().Select(r => r.Table.Columns.Cast<DataColumn>().ToDictionary(c => c.ColumnName, c => r[c])).ToList() - 这样绕过
DataTable本身,System.Text.Json能自然处理null和基础类型 - 性能略低(多一次内存投影),但代码更可控,不依赖第三方包
时间字段的格式容易被忽略
DateTime列默认序列化成ISO 8601(如"2024-03-15T14:22:03.123Z"),但很多老系统前端只认"yyyy-MM-dd HH:mm:ss"这种格式,直接解析会出错或显示NaN。
- Newtonsoft.Json:用
DateFormatString = "yyyy-MM-dd HH:mm:ss" - System.Text.Json:加
JsonConverter<DateTime>或全局DateTimeConverter - 注意时区——
DateTimeKind.Unspecified不会带时区标识,但前端Date构造函数可能按本地时区解释,导致偏差1天
datetime2或Oracle的DATE,类型隐式转换的坑比想象中多。










