为何示例中Newtonsoft.Json可用而System.Text.Json失效?
DataTable转实体:System.Text.Json替代Newtonsoft.Json的问题解决
问题说明
从数据库读取数据到DataTable后,通过JSON序列化再反序列化为List<TodoItem>的代码,用Newtonsoft.Json能正常运行,但切换到System.Text.Json时抛出异常,提示不支持'System.Type'实例的序列化与反序列化。
原代码
using DataModelsClassLibrary1; using Microsoft.Data.SqlClient; using System.Data; using System.Text.Json; namespace DataLayerClassLibrary1 { public class Class1 { public static List<TodoItem> GetAllTodos() { var connString = @"Server=(localdb)\MSSQLLocalDB;Database=Database1;Trusted_Connection=True;"; var dt = new DataTable(); List<TodoItem> todos = new List<TodoItem>(); using (var con = new SqlConnection(connString)) { SqlCommand command = new SqlCommand("SELECT * FROM Todo", con); con.Open(); SqlDataReader reader = command.ExecuteReader(); dt.Load(reader); if (dt.Rows.Count > 0) { var serializedMyObjects = JsonSerializer.Serialize(dt); todos = JsonSerializer.Deserialize<List<TodoItem>>(serializedMyObjects); } con.Close(); } return todos; } } }
错误信息
未处理的异常。System.NotSupportedException: 不支持'System.Type'实例的序列化与反序列化。路径: $.Columns.DataType。
---> System.NotSupportedException: Serialization and deserialization of 'System.Type' instances are not supported.
(后续堆栈信息省略)
解决方法
方法1:直接从SqlDataReader映射实体(推荐)
完全跳过DataTable和JSON的中间步骤,直接读取DataReader并映射到实体,性能更优,也避免序列化问题:
public static List<TodoItem> GetAllTodos() { var connString = @"Server=(localdb)\MSSQLLocalDB;Database=Database1;Trusted_Connection=True;"; List<TodoItem> todos = new List<TodoItem>(); using (var con = new SqlConnection(connString)) { SqlCommand command = new SqlCommand("SELECT * FROM Todo", con); con.Open(); // 用using包裹DataReader,自动释放资源 using (SqlDataReader reader = command.ExecuteReader()) { // 获取字段索引,避免重复查找 int idIndex = reader.GetOrdinal("Id"); int titleIndex = reader.GetOrdinal("Title"); int isCompletedIndex = reader.GetOrdinal("IsCompleted"); while (reader.Read()) { todos.Add(new TodoItem { Id = reader.GetInt32(idIndex), Title = reader.IsDBNull(titleIndex) ? null : reader.GetString(titleIndex), IsCompleted = reader.GetBoolean(isCompletedIndex) // 根据实体字段补充其他映射 }); } } } return todos; }
方法2:自定义DataTable的JSON转换器
如果必须保留DataTable转JSON的流程,需要自定义转换器跳过DataTable中不支持序列化的Type类型(比如列的DataType属性):
public class DataTableJsonConverter : JsonConverter<DataTable> { public override DataTable Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { // 若需要反序列化DataTable可自行实现,此处省略 throw new NotImplementedException("DataTable反序列化逻辑需按需编写"); } public override void Write(Utf8JsonWriter writer, DataTable value, JsonSerializerOptions options) { writer.WriteStartArray(); // 只序列化行数据,跳过列的元数据 foreach (DataRow row in value.Rows) { writer.WriteStartObject(); foreach (DataColumn col in value.Columns) { writer.WritePropertyName(col.ColumnName); // 处理DBNull值 var val = row.IsNull(col) ? null : row[col]; JsonSerializer.Serialize(writer, val, options); } writer.WriteEndObject(); } writer.WriteEndArray(); } }
使用转换器的代码:
if (dt.Rows.Count > 0) { var options = new JsonSerializerOptions(); options.Converters.Add(new DataTableJsonConverter()); var serializedMyObjects = JsonSerializer.Serialize(dt, options); todos = JsonSerializer.Deserialize<List<TodoItem>>(serializedMyObjects); }
方法3:用LINQ直接转换DataTable到实体
利用DataTable的AsEnumerable()扩展方法,通过LINQ将行数据映射为实体:
if (dt.Rows.Count > 0) { todos = dt.AsEnumerable() .Select(row => new TodoItem { Id = row.Field<int>("Id"), Title = row.Field<string>("Title"), IsCompleted = row.Field<bool>("IsCompleted") // 补充其他字段映射 }) .ToList(); }
内容的提问来源于stack exchange,提问作者Rod
相关产品推荐
相关产品推荐

