使用C#获取REST API数据并写入SQL表时的JSON反序列化报错问题求助
解决C#中嵌套JSON反序列化DataTable失败并插入SQL的问题
Hey Edi, let's break down what's going wrong and fix this step by step!
问题原因
你遇到的JsonSerializationException是因为API返回的JSON结构是嵌套的:content数组里的每个元素不是直接包含name/age/date的平面对象,而是包裹在一个user对象里。DataTable无法直接解析这种嵌套结构,它期望每个数组元素都是直接的键值对(对应表格的列和值),所以才会报错"Unexpected JSON token when reading DataTable: StartObject"。
解决方案步骤
我们先通过实体类来正确解析嵌套的JSON,再提取数据插入SQL,同时优化数据库操作的效率。
1. 定义对应JSON结构的实体类
先创建几个简单的类来匹配API返回的JSON,这样反序列化会更清晰、不容易出错:
// 对应每个user的具体数据 public class UserData { public string name { get; set; } public string age { get; set; } public string date { get; set; } } // 对应content数组里的每个元素(包裹user的对象) public class ContentItem { public UserData user { get; set; } } // 对应整个API返回的JSON结构 public class ApiResponse { public List<ContentItem> content { get; set; } // 如果需要用到其他字段(比如ReturnCode、@count),可以在这里添加对应的属性 }
2. 修改API调用与反序列化代码
替换你原来错误的反序列化代码,改用实体类来解析JSON:
protected void btnAPI_OnClick(object sender, EventArgs e) { ServicePointManager.Expect100Continue = true; ServicePointManager.SecurityProtocol = (SecurityProtocolType)3072; using (WebClient client = new WebClient()) { client.Credentials = new NetworkCredential(username, password); string json = client.DownloadString("https://XXXX/XXXX001?view=expand"); // 反序列化到我们定义的ApiResponse类,正确解析嵌套结构 var apiResponse = JsonConvert.DeserializeObject<ApiResponse>(json); // 提取所有user数据,得到一个UserData的列表 var userList = apiResponse.content.Select(item => item.user).ToList(); // 插入SQL表(优化后的数据库操作) InsertUsersToSql(userList); } }
3. 优化SQL插入方法
原来的代码在循环里重复打开/关闭数据库连接,这会降低效率。我们用using语句自动管理连接生命周期,同时避免重复代码:
private void InsertUsersToSql(List<UserData> userList) { // 替换成你的数据库连接字符串,或者复用你的connection()方法返回的连接 string connectionString = "你的SQL连接字符串"; foreach (var user in userList) { using (SqlConnection sqlConn = new SqlConnection(connectionString)) { string qry = "INSERT INTO myTable (name, age, date) VALUES(@name, @age, @date)"; sqlConn.Open(); using (SqlCommand cmd = new SqlCommand(qry, sqlConn)) { // 添加参数,避免SQL注入 cmd.Parameters.AddWithValue("@name", user.name); cmd.Parameters.AddWithValue("@age", user.age); // 注意:如果SQL表的date字段是datetime类型,需要把字符串转成DateTime对象 cmd.Parameters.AddWithValue("@date", DateTime.Parse(user.date)); cmd.ExecuteNonQuery(); } // using块结束后,连接会自动关闭并归还到连接池 } } }
额外优化建议(可选)
如果你的API返回大量数据(比如几百条以上),用SqlBulkCopy批量插入会比循环插入高效得多。你可以把userList转换成DataTable,然后批量写入:
private void BulkInsertUsersToSql(List<UserData> userList) { string connectionString = "你的SQL连接字符串"; // 把userList转换成DataTable DataTable dt = new DataTable(); dt.Columns.Add("name", typeof(string)); dt.Columns.Add("age", typeof(string)); dt.Columns.Add("date", typeof(DateTime)); foreach (var user in userList) { dt.Rows.Add(user.name, user.age, DateTime.Parse(user.date)); } // 批量插入 using (SqlConnection sqlConn = new SqlConnection(connectionString)) { sqlConn.Open(); using (SqlBulkCopy bulkCopy = new SqlBulkCopy(sqlConn)) { bulkCopy.DestinationTableName = "myTable"; // 映射列名(如果DataTable列名和SQL表列名一致,这步可以省略) bulkCopy.ColumnMappings.Add("name", "name"); bulkCopy.ColumnMappings.Add("age", "age"); bulkCopy.ColumnMappings.Add("date", "date"); bulkCopy.WriteToServer(dt); } } }
关键注意点
- 日期类型转换:如果你的SQL表中
date字段是datetime/datetime2类型,一定要把JSON里的字符串转换成DateTime对象,避免插入失败或格式错误。 - SQL注入防护:始终使用参数化查询(
AddWithValue),不要直接拼接SQL字符串。 - 连接管理:用
using语句管理SqlConnection和SqlCommand,确保资源自动释放,避免连接泄漏。
内容的提问来源于stack exchange,提问作者Edi Isma
相关产品推荐
相关产品推荐

