C#实现CSV转JSON分批调用API后回写完整CSV方案咨询
C# 基于CsvHelper实现CSV拆分提交、结果回导方案
核心实现逻辑
1. 原始数据读取优化
你当前的写法直接将全量数据ToList()加载到内存,对于1.5-3万行的规模完全可用,但建议将结果转换为以number为键的字典,后续API结果返回时可以O(1)时间复杂度匹配到原始记录,避免循环遍历的性能损耗,同时在读取阶段增加重复number校验,防止后续匹配错乱。
2. 分批生成待提交JSON
- 首先定义API提交对应的模型,包含你要求的固定起始段、
number、taxIdentifier、末尾固定2个字符字段 - 按单批10000条的阈值对原始记录做切分,每批映射为提交模型集合后序列化为JSON,单独存储为文件即可,文件名可以加批次序号区分
- 如果固定前后缀是按批次动态变化的,在切分批次时按业务规则赋值即可,无需写死
3. API结果匹配与最终CSV导出
- 拿到API返回结果后,遍历每条返回数据,用
number字段从之前构建的原始数据字典中取出对应完整记录 - 给原始记录类扩展属性存储API返回的校验结果(比如校验状态、错误信息等),匹配时直接赋值即可
- 所有记录匹配完成后,用CsvWriter直接将全量记录写入新CSV,会自动导出所有原始列+你新增的校验结果列
- 匹配过程中如果碰到API返回的number在原始数据中不存在的情况,写入错误日志即可,不要直接中断整个导出流程,最后统一处理异常数据
核心参考代码
using System.Text.Json; using CsvHelper; using System.Globalization; protected void Button2_Click(object sender, EventArgs e) { string sourcePath = @"E:\Temp\Spelpaus\Malmö2022fromNeon.csv"; string outputDir = @"E:\Temp\Spelpaus\"; // 读取原始CSV,构建number为键的快速查询字典 var originalRecordMap = new Dictionary<string, RocketLaunch>(); using (var streamreader = new StreamReader(sourcePath)) using (var csvReader = new CsvReader(streamreader, CultureInfo.InvariantCulture)) { foreach (var record in csvReader.GetRecords<RocketLaunch>()) { if (!originalRecordMap.TryAdd(record.number, record)) { throw new Exception($"检测到重复number值:{record.number},请先清理源数据"); } } } // 按每批10000条拆分生成提交JSON int batchSize = 10000; var allRecords = originalRecordMap.Values.ToList(); int batchCount = (int)Math.Ceiling(allRecords.Count * 1.0 / batchSize); for (int i = 0; i < batchCount; i++) { var batchSubmitData = allRecords .Skip(i * batchSize) .Take(batchSize) .Select(r => new ApiSubmitItem { number = r.number, taxIdentifier = r.taxIdentifier // 固定前后缀字段按业务要求赋值即可 }) .ToList(); // 序列化为JSON文件 var jsonContent = JsonSerializer.Serialize(batchSubmitData, new JsonSerializerOptions { WriteIndented = true }); File.WriteAllText(Path.Combine(outputDir, $"submit_batch_{i+1}.json"), jsonContent); } // --- 以下是API结果匹配、导出逻辑,等你拿到接口返回后放开即可 --- // var apiCheckResults = 你的API调用方法,返回带number和校验结果的集合 // foreach (var result in apiCheckResults) // { // if (originalRecordMap.TryGetValue(result.number, out var sourceRecord)) // { // sourceRecord.CheckStatus = result.Status; // sourceRecord.CheckMessage = result.ErrorMsg; // } // else // { // File.AppendAllText(Path.Combine(outputDir, "match_error.log"), $"未找到number对应原始记录:{result.number}{Environment.NewLine}"); // } // } // // 导出最终结果CSV // using (var writer = new StreamWriter(Path.Combine(outputDir, "final_check_result.csv"))) // using (var csvWriter = new CsvWriter(writer, CultureInfo.InvariantCulture)) // { // csvWriter.WriteRecords(allRecords); // } } // API提交数据模型 public class ApiSubmitItem { // 替换为你要求的指定起始段字段名、固定值 public string FixedPrefix { get; set; } = "指定起始段固定值"; public string number { get; set; } public string taxIdentifier { get; set; } // 替换为你要求的末尾2个指定字符字段名、固定值 public string FixedSuffix { get; set; } = "末尾2个指定字符"; } // 原始CSV模型,扩展API结果字段 public class RocketLaunch { public string number { get; set; } public string surname { get; set; } public string forename { get; set; } public string emailAddress { get; set; } public string taxIdentifier { get; set; } // 以下为API返回的校验结果字段,按实际接口返回内容调整 public string CheckStatus { get; set; } public string CheckMessage { get; set; } }
注意事项
- 如果你用的是.NET Framework环境没有内置System.Text.Json,直接换成Newtonsoft.Json做JSON序列化即可,业务逻辑不需要调整
- Web环境下不要写死本地磁盘路径,用
Server.MapPath映射站点内目录,避免出现磁盘权限问题 - 3万条数据规模很小,上述逻辑没有性能瓶颈,如果后续数据量涨到10万以上,再考虑改成边读边处理的流式逻辑即可
- 如果API有提交频率限制,在批次提交的逻辑里加对应延迟就可以,不需要改动核心处理流程
内容的提问来源于stack exchange,提问作者user17461080
相关产品推荐
相关产品推荐

