You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何示例中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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.03 22:50:29