简介:这是一份面向C#开发者与数据处理人员的Excel转JSON工具源码包,针对需要将结构化表格数据导入Web应用或NoSQL数据库的场景,提供可直接运行的转换方案。压缩包共36个文件,约193KB,包含6个cs源码文件、1个sln解决方案与csproj工程文件、3个exe可执行程序及配套dll、pdb调试文件,另有config配置、resx资源与json示例等,工程结构完整,便于二次开发与调试。资源围绕OleDbConnection读取Excel、DataTable承载数据、JavaScriptSerializer序列化为JSON的完整链路展开,并提示了.xls与.xlsx连接字符串差异、异常处理、大数据分批读取及日期格式清洗等实用要点。目前已有936人学习下载,适合初、中级C#开发者参考,可快速掌握文件I/O与数据序列化技巧,也可将代码封装为独立工具类在项目中复用。
1. 从 Excel 到 JSON 的 C# 转换工具:为什么我建议你手头常备一份源码
做后端或工具链开发的人,迟早会撞上同一个需求:业务方丢来一个 Excel,说“把这个表转成 JSON,接口要用”。第一次你可能手动复制粘贴,第二次写个一次性脚本,第三次就会发现——每次字段名不一样、每次合并单元格的坑都要重踩一遍。这份 C# 实现的 Excel 转 JSON 工具加源码,解决的正是这个反复出现的脏活。它把读取、映射、序列化三步固化下来,适合经常要处理配置表、字典表、测试数据的 .NET 开发者。拿到源码后你能直接改字段映射规则,而不是被某个在线转换网站的黑匣子卡住。
2. 先搞懂 Excel 转 JSON 在 C# 里到底走哪条路
2.1 两条主流技术路线:OLE DB 与 Open XML SDK
在 C# 里读 Excel,绕不开两个选择。第一条是老牌的 OLE DB / ACE 驱动,通过Microsoft.ACE.OLEDB把工作表当数据库表查,写 SQL 就能拿数据。优点是代码短、上手快,缺点是必须装对应版本的 Access Database Engine,32 位和 64 位还对不上,部署到服务器上经常翻车。第二条是 Open XML SDK(DocumentFormat.OpenXml),直接解析.xlsx的 XML 结构,不依赖 Office 或驱动,纯托管代码,跨平台也能跑。这份工具的源码走的是 Open XML 路线,原因很实际:你不可能要求每台部署机器都装一个 ACE 驱动,尤其是容器环境。
选型上我的判断标准很简单——如果只是本地临时转一次,OLE DB 够用;如果这个转换要进 CI、要进服务、要给别人用,就老老实实上 Open XML。代价是代码量会多,但换来的是可控和可移植。
2.2 读取工作表与共享字符串表
.xlsx本质是个 zip 包,里面xl/worksheets/sheet1.xml存单元格,xl/sharedStrings.xml存所有文本值。单元格里如果是字符串,<v>存的不是文本本身,而是共享字符串表的索引。很多人第一次解析发现拿到的全是数字,就是漏了这一步回查。下面这段是读取的核心逻辑:
using DocumentFormat.OpenXml.Packaging; using DocumentFormat.OpenXml.Spreadsheet; public static List<Dictionary<string, string>> ReadSheet(string filePath, string sheetName) { var rows = new List<Dictionary<string, string>>(); using (var doc = SpreadsheetDocument.Open(filePath, false)) { var wbPart = doc.WorkbookPart; // 按名字找到对应工作表,避免默认取第一个 var sheet = wbPart.Workbook.Descendants<Sheet>() .FirstOrDefault(s => s.Name == sheetName); if (sheet == null) throw new ArgumentException($"找不到工作表: {sheetName}"); var wsPart = (WorksheetPart)wbPart.GetPartById(sheet.Id); var sharedStrings = wbPart.SharedStringTablePart?.SharedStringTable; var sheetData = wsPart.Worksheet.Elements<SheetData>().First(); var allRows = sheetData.Elements<Row>().ToList(); if (allRows.Count == 0) return rows; // 第一行当表头 var headers = allRows[0].Elements<Cell>() .Select(c => GetCellValue(c, sharedStrings)).ToList(); foreach (var row in allRows.Skip(1)) { var dict = new Dictionary<string, string>(); var cells = row.Elements<Cell>().ToList(); for (int i = 0; i < headers.Count; i++) { // 单元格可能因空值被跳过,需要按列引用对齐 var cell = cells.FirstOrDefault(c => GetColumnIndex(c.CellReference) == i); dict[headers[i]] = cell == null ? "" : GetCellValue(cell, sharedStrings); } rows.Add(dict); } } return rows; }逻辑说明:先定位工作表,再拿共享字符串表备用。表头取第一行,数据行逐行映射。这里有个关键点——Excel 里空单元格不会生成<c>节点,所以不能简单按顺序取,必须用CellReference(比如B3)算出列号再对齐,否则一旦某行中间有空值,后面所有列全部错位。GetColumnIndex就是把B3里的B转成数字 1 的小工具,GetCellValue负责判断DataType是否为SharedString并回查。
2.3 把行数据序列化成 JSON
拿到List<Dictionary<string, string>>之后,序列化本身不难,难的是类型处理。Excel 里所有东西读出来都是字符串,但接口要的 JSON 里数字得是数字、布尔得是布尔。常见做法是加一层类型推断或让调用方传 schema。这份源码用的是显式类型映射,稳妥:
using System.Text.Json; public static string ToJson(List<Dictionary<string, string>> rows, Dictionary<string, string> typeMap) { var result = new List<Dictionary<string, object>>(); foreach (var row in rows) { var obj = new Dictionary<string, object>(); foreach (var kv in row) { // 根据 typeMap 决定转换目标类型,未声明的按字符串处理 obj[kv.Key] = typeMap.TryGetValue(kv.Key, out var t) ? t switch { "int" => long.TryParse(kv.Value, out var l) ? l : 0, "double" => double.TryParse(kv.Value, out var d) ? d : 0.0, "bool" => kv.Value == "1" || kv.Value.Equals("true", StringComparison.OrdinalIgnoreCase), _ => kv.Value } : kv.Value; } result.Add(obj); } // WriteIndented 方便人工核对,生产环境可关掉 return JsonSerializer.Serialize(result, new JsonSerializerOptions { WriteIndented = true, Encoder = System.Text.Encodings.Web.JavaScriptEncoder.UnsafeRelaxedJsonEscaping }); }参数说明:typeMap是字段名到类型的字典,比如{"age":"int","score":"double","enabled":"bool"}。没在 map 里的字段一律当字符串,避免误转。UnsafeRelaxedJsonEscaping是为了让中文不被转成\uXXXX,否则导出的 JSON 里全是转义码,人工看很痛苦。WriteIndented在调试时开着,正式输出可以设 false 省体积。
3. 把工具跑起来:从命令行到批量转换
3.1 项目结构与依赖安装
源码包解压后是一个标准的 .NET 控制台项目,核心就三个文件:Program.cs入口、ExcelReader.cs读取、JsonWriter.cs序列化。依赖只有一个 NuGet 包:
dotnet add package DocumentFormat.OpenXml --version 3.0.0装完直接dotnet build就能编译。如果你的项目是 .NET Framework 4.x,Open XML SDK 2.5 以上都支持,但要注意System.Text.Json需要额外引 NuGet 包,或者改用Newtonsoft.Json。我一般在新项目里统一用System.Text.Json,少一个依赖是一个。
3.2 命令行参数怎么传
工具设计成命令行调用,方便塞进批处理或 CI 脚本。参数就四个:
| 参数 | 含义 | 是否必填 | 示例 |
|---|---|---|---|
-i | 输入 Excel 路径 | 是 | -i data.xlsx |
-s | 工作表名 | 否,默认第一个 | -s Sheet1 |
-o | 输出 JSON 路径 | 否,默认同名 .json | -o out.json |
-t | 类型映射文件 | 否 | -t types.json |
调用示例:
dotnet run -- -i ./config.xlsx -s "角色表" -o ./role.json -t ./types.jsontypes.json长这样:
{ "id": "int", "level": "int", "rate": "double", "isBoss": "bool" }这样跑完,role.json里id就是数字而不是"1001"。如果不传-t,所有字段都是字符串,接口那边可能因为类型不匹配报错,这是最常见的对接问题。
3.3 批量转换多个工作表
实际项目里一个 Excel 往往有十几张表,一张张转太慢。源码里留了个-all开关,遍历工作簿里所有 sheet,每张输出一个同名 JSON:
if (args.Contains("-all")) { foreach (var sheetName in GetAllSheetNames(inputPath)) { var data = ReadSheet(inputPath, sheetName); var json = ToJson(data, typeMap); // 输出文件名用工作表名,避免覆盖 File.WriteAllText($"{outputDir}/{sheetName}.json", json); } }注意工作表名里可能有空格或特殊字符,直接当文件名在 Windows 上会出问题,稳妥做法是做一次文件名净化,把非法字符替换成下划线。这个细节源码里已经处理了,但如果你自己改,别忘了。
4. 避坑指南:Excel 转 JSON 最容易翻车的五个地方
4.1 合并单元格导致数据只出现在第一格
现象:表头是合并的,比如“用户信息”横跨三列,读出来只有第一列有值,后面两列是空。原因:合并单元格在 XML 里只有左上角那个格子存值,其余格子根本不存在。解决:要么在读取时根据MergeCells信息向上/向左填充,要么在转换前要求业务方取消合并。我一般选后者,因为填充逻辑一旦遇到复杂合并很容易出错,不如从源头规范。
4.2 日期变成一串数字
现象:Excel 里明明是2024-01-15,读出来是45306。原因:Excel 内部日期存的是从 1900-01-01 起的天数,Open XML 读到的就是原始数字。解决:判断单元格StyleIndex对应的数字格式是否为日期,是的话用DateTime.FromOADate转换。但FromOADate对 1900 年之前的日期有 bug,业务数据一般不会碰到,知道边界就行。
4.3 大文件内存爆掉
现象:几十万行的 Excel 一转就 OOM。原因:SpreadsheetDocument.Open默认把整个 DOM 加载进内存。解决:改用OpenXmlReader流式读取,逐行处理,内存占用能降一个数量级。代价是代码复杂一些,不能随机访问。我的经验是超过 5 万行就上流式,以下用 DOM 无所谓。
4.4 数字精度丢失
现象:Excel 里123456789012345678这种长整数,转出来末尾几位变 0。原因:Excel 本身只保证 15 位有效数字,超过就丢精度,这是 Excel 的锅不是代码的锅。解决:长 ID 类字段在 Excel 里就存成文本格式,或者干脆别用 Excel 存这种数据。
4.5 空行和尾部空列混进结果
现象:JSON 里多出一堆空对象。原因:Excel 的UsedRange经常比实际数据大,尾部有看不见的空行。解决:读取后过滤掉所有值都为空的字典,源码里加了一行rows.RemoveAll(r => r.Values.All(string.IsNullOrWhiteSpace))。这行看着简单,但少了它下游解析经常报莫名其妙的错。
5. 进阶技巧:让转换结果直接对接接口和配置系统
5.1 用 JSON Schema 做输出校验
转出来的 JSON 对不对,别靠肉眼。我习惯在转换后跑一遍 Schema 校验,尤其是字段类型和必填项。用NJsonSchema几行就能搞定:
var schema = await NJsonSchema.JsonSchema.FromFileAsync("schema.json"); var errors = schema.Validate(jsonString); if (errors.Count > 0) throw new Exception($"JSON 校验失败: {string.Join(", ", errors.Select(e => e.ToString()))}");这样一旦 Excel 里某列类型不对,转换阶段就拦住了,不会等到接口报 500 才发现。Schema 文件本身可以从接口的 DTO 反向生成,保持单一事实来源。
5.2 把转换嵌进构建流程
如果 JSON 是给前端或客户端当配置用的,完全可以把它做成构建的一步。在.csproj里加一个 MSBuild Target,每次编译前自动跑转换:
<Target Name="ExcelToJson" BeforeTargets="BeforeBuild"> <Exec Command="dotnet run --project tools/ExcelToJson -- -i config/data.xlsx -all -o wwwroot/config/" /> </Target>这样策划改完 Excel 提交,CI 一跑,JSON 自动更新,省掉手动转换和忘记转换的麻烦。注意Exec的工作目录,路径写错会静默失败,建议加ContinueOnError="false"让它出错就中断构建。
5.3 一个我踩过的教训
早期我图省事,直接在读取时把表头硬编码成字段名,结果业务方改了一次列名,整个转换静默产出空字段,接口返回一堆 null,排查了半天才发现是表头对不上。从那以后我每次改完 Excel 模板,都强制走一遍“转换 → Schema 校验 → 抽样比对”三步,哪怕只改了一个字。工具是死的,流程是活的,源码给你的是可控性,但用不用好还是看习惯。希望这份工具和源码能帮你少加几次班。
本文还有配套的精品资源,点击获取