C#实现Excel转JSON工具:游戏配置表批量转换与类型推断
发布时间:2026/10/20 3:02:08 锦皓数字建站

简介这是一份面向C#开发者与数据处理初学者的Excel转JSON工具源码包针对需要将结构化表格数据导入Web应用或NoSQL数据库的场景提供可直接运行的转换方案。压缩包共36个文件约193KB包含6个cs源码文件、1个sln解决方案与csproj工程文件、3个exe可执行程序及配套dll、pdb调试文件另有config配置、resx资源与json示例等工程结构完整便于直接编译调试。源码演示了通过OleDbConnection读取Excel、用OleDbDataAdapter填充DataTable再借助JavaScriptSerializer序列化为JSON的完整链路并提示了.xls与.xlsx连接字符串差异、异常处理、大数据分批读取及日期格式清洗等要点。目前已有936人学习下载适合想快速掌握C#文件I/O与数据序列化、并需要可复用转换工具的开发者参考借鉴。1. 从一份 Excel 配置表说起这个 C# 小工具到底解决什么问题策划丢过来一个 200 行的 Excel里面有技能 ID、名称、描述、冷却时间、伤害系数让你转成 JSON 塞进 Unity 或后端服务里。手动复制粘贴改一版就要重来一次字段一多眼睛都看花。这个用 C# 写的 Excel 转 JSON 工具就是冲着这个场景来的读一张.xlsx按表头映射字段输出一份干净的.json支持批量转换和类型推断。它适合谁做游戏配置表、做后台字典数据、做前端 mock 数据的开发者尤其是那些不想为一次转换去装一堆 Python 环境或在线工具的人。工具本身是 WinForm 或控制台形态源码完整拿到就能编译改字段映射规则也不用从零啃。2. 技术选型与项目结构为什么用 C# 而不是脚本2.1 选型逻辑C# 做配置转换的天然优势很多人第一反应是 Python 的 pandas读 Excel 确实方便但问题在于分发。你给策划一个.exe双击就能跑给一个 Python 脚本对方要先装解释器、再装 openpyxl中间任何一步报错都来找你。C# 编译成单文件后依赖可以内嵌Windows 上零配置运行这对内部工具来说是刚需。这个项目读取 Excel 用的是EPPlus或NPOI两者都是 .NET 生态里成熟的库。EPPlus 对.xlsx支持好、API 简洁NPOI 则兼容老的.xls格式。常见做法是优先用 EPPlus因为现在策划基本都用新版 Excel。JSON 序列化用Newtonsoft.Json虽然 .NET 自带的System.Text.Json也能用但 Newtonsoft 对动态类型和自定义格式化的支持更顺手尤其是处理“空单元格要不要输出 null”这种细节时。项目结构一般是这样组织的ExcelToJson/ ├── Program.cs // 入口解析命令行参数或启动窗体 ├── Core/ │ ├── ExcelReader.cs // 读取 Excel返回行数据 │ ├── JsonWriter.cs // 把行数据写成 JSON │ └── TypeInferrer.cs // 推断单元格类型int/double/string/bool ├── Models/ │ └── SheetConfig.cs // 配置类表头行号、起始列、忽略列 ├── Config/ │ └── mapping.json // 字段映射规则可选 └── ExcelToJson.csproj这个分层的好处是读 Excel 的逻辑和写 JSON 的逻辑解耦换一个 Excel 库只需要改ExcelReader换 JSON 库只改JsonWriter。TypeInferrer单独抽出来是因为类型推断是最容易出玄学问题的地方——Excel 里一个单元格看着是数字读出来可能是double也可能是string还可能是DateTime。2.2 核心读取逻辑从 Sheet 到内存对象先看ExcelReader的关键代码。这里假设用 EPPlus读取第一个 Sheet跳过表头把每一行转成Dictionarystring, objectusing OfficeOpenXml; public class ExcelReader { // 读取指定 Sheet返回行数据列表 public ListDictionarystring, object Read(string filePath, int headerRow 1) { var result new ListDictionarystring, object(); using var package new ExcelPackage(new FileInfo(filePath)); var sheet package.Workbook.Worksheets[0]; // 默认取第一个 Sheet int rowCount sheet.Dimension.Rows; int colCount sheet.Dimension.Columns; // 先读表头建立列索引到字段名的映射 var headers new string[colCount 1]; for (int col 1; col colCount; col) { headers[col] sheet.Cells[headerRow, col].Text?.Trim() ?? $col{col}; } // 从表头下一行开始读数据 for (int row headerRow 1; row rowCount; row) { var dict new Dictionarystring, object(); for (int col 1; col colCount; col) { var cell sheet.Cells[row, col]; // 跳过空行整行都没有有效值 if (string.IsNullOrWhiteSpace(cell.Text)) continue; dict[headers[col]] cell.Value; // 保留原始类型 } if (dict.Count 0) result.Add(dict); } return result; } }这段代码有几个关键点。sheet.Dimension拿到的是有效数据区域避免遍历整个 104 万行。cell.Value返回的是object可能是double、string、bool、DateTime保留原始类型是为了后面类型推断用。cell.Text是显示文本用来判断空单元格更可靠因为有些单元格值是null但格式还在。参数说明headerRow默认 1如果你的表前几行是说明文字改成 2 或 3 即可。Worksheets[0]是取第一个 Sheet如果配置表在第二个 Sheet改成Worksheets[SheetName]或索引 1。2.3 类型推断与 JSON 输出把 object 变成正确的 JSON 类型读出来的object直接序列化会出问题Excel 里所有数字都是double1会变成1.0ID 字段就毁了。所以需要TypeInferrer做一次转换public static class TypeInferrer { // 根据单元格原始值和列名推断目标类型 public static object Infer(object rawValue, string columnName) { if (rawValue null) return null; // 如果列名以 Id 结尾强制转成整数 if (columnName.EndsWith(Id, StringComparison.OrdinalIgnoreCase)) { if (double.TryParse(rawValue.ToString(), out var idVal)) return (int)idVal; } // double 且是整数值转 int if (rawValue is double d) { if (d Math.Floor(d) !double.IsInfinity(d)) return (int)d; return d; } // bool 直接返回 if (rawValue is bool b) return b; // DateTime 转成 ISO 字符串避免时区问题 if (rawValue is DateTime dt) return dt.ToString(yyyy-MM-dd HH:mm:ss); return rawValue.ToString(); } }逻辑说明优先看列名Id结尾的强制整数这是业务约定。然后看值本身double如果是整数值就转int否则保留double。DateTime转字符串是为了 JSON 里可读也避免不同平台解析差异。最后兜底转字符串。JsonWriter就简单了用 Newtonsoft 序列化using Newtonsoft.Json; public class JsonWriter { public void Write(ListDictionarystring, object data, string outputPath) { // 先做类型推断 var converted data.Select(row row.ToDictionary( kv kv.Key, kv TypeInferrer.Infer(kv.Value, kv.Key) ) ).ToList(); // 格式化输出方便人工检查 string json JsonConvert.SerializeObject(converted, Formatting.Indented); File.WriteAllText(outputPath, json, System.Text.Encoding.UTF8); } }Formatting.Indented让 JSON 带缩进方便策划直接看。Encoding.UTF8不加的话中文会乱码这是血泪经验。如果数据量大、不需要人工看改成Formatting.None能减小文件体积。3. 跑起来从编译到批量转换的完整操作3.1 环境准备与编译需要 .NET SDK版本建议 6.0 或以上。如果你用 Visual Studio直接打开.csproj还原 NuGet 包即可。命令行方式# 进入项目目录 cd ExcelToJson # 还原依赖 dotnet restore # 编译 Release 版本 dotnet build -c Release # 运行假设是控制台程序接收输入文件路径 dotnet run --project ExcelToJson.csproj -- D:\config\skill.xlsx编译产物在bin/Release/net6.0/下ExcelToJson.exe可以直接拷给策划用。如果想让对方不装 .NET 运行时发布时加--self-containeddotnet publish -c Release -r win-x64 --self-contained true -p:PublishSingleFiletrue这样生成一个独立的 exe体积大概 60-70MB但省去了环境配置的沟通成本。3.2 批量转换与命令行参数单文件转换只是基础实际项目里往往一个目录下几十张表。常见做法是在Program.cs里支持目录扫描static void Main(string[] args) { // 参数输入路径文件或目录输出目录 string input args.Length 0 ? args[0] : .; string outputDir args.Length 1 ? args[1] : ./output; Directory.CreateDirectory(outputDir); var files new Liststring(); if (Directory.Exists(input)) files.AddRange(Directory.GetFiles(input, *.xlsx)); else if (File.Exists(input)) files.Add(input); var reader new ExcelReader(); var writer new JsonWriter(); foreach (var file in files) { var data reader.Read(file); string outName Path.GetFileNameWithoutExtension(file) .json; string outPath Path.Combine(outputDir, outName); writer.Write(data, outPath); Console.WriteLine($转换完成: {file} - {outPath}); } }参数说明第一个参数是输入可以是单个.xlsx文件也可以是一个目录。第二个参数是输出目录默认./output。Directory.GetFiles只扫当前层不递归子目录如果需要递归改成SearchOption.AllDirectories。3.3 输出结果验证转换完成后打开 JSON 检查三件事字段名是否和表头一致、数字类型是否正确ID 没有小数点、中文是否乱码。如果 ID 出现1.0说明TypeInferrer没生效检查列名是否以Id结尾或者手动在映射配置里指定类型。提示Excel 里如果单元格格式是“文本”读出来的值可能是字符串123TypeInferrer里double.TryParse能处理但如果是123abc就会失败返回原字符串。这种情况需要在 Excel 里先清理数据。4. 避坑与常见问题那些让我加班到凌晨的细节4.1 空单元格与合并单元格的坑现象转换出来的 JSON 里某些字段缺失或者值跑到了错误的字段上。原因通常是合并单元格。EPPlus 读取合并区域时只有左上角单元格有值其他位置是null。如果表头有合并headers[col]会变成空字符串导致字段名丢失。解决办法是在读表头时遇到空字符串就往前找最近的非空表头for (int col 1; col colCount; col) { string header sheet.Cells[headerRow, col].Text?.Trim(); if (string.IsNullOrEmpty(header)) header headers[col - 1]; // 继承前一列的表头 headers[col] header; }4.2 数字精度丢失与科学计数法现象Excel 里填的1234567890123转出来变成1.234567890123E12或者末尾几位变 0。原因是 Excel 内部用double存储数字超过 15 位有效数字就会丢精度。解决方式ID 类字段在 Excel 里设置成文本格式读取时cell.Text拿字符串不要用cell.Value。或者在TypeInferrer里对Id字段直接用long.TryParse处理字符串。4.3 中文乱码与 BOM 头现象JSON 文件用记事本打开正常用某些编辑器打开中文变问号。原因是File.WriteAllText默认不带 BOM部分老编辑器识别不了 UTF-8。解决显式指定new UTF8Encoding(true)带 BOM或者统一要求用 VS Code 打开。我一般会在输出时加 BOM省得策划来找。4.4 大文件内存溢出现象一张几万行的 Excel 转换时程序卡死或抛OutOfMemoryException。原因是ExcelPackage把整个文件加载到内存加上ListDictionary又复制一份。解决改用流式读取或者分批处理。EPPlus 支持ExcelPackage.LicenseContext设置后按行读取但更简单的做法是限制单次转换的行数或者换用 NPOI 的SXSSF模式仅写。读方面几万行一般还能扛住几十万行就要考虑分 Sheet 了。4.5 字段名大小写与特殊字符现象JSON 里字段名是SkillID但代码里按skillId取取不到。原因是 Excel 表头大小写和代码约定不一致。解决在ExcelReader里统一转成 camelCase或者加一层映射配置。特殊字符如空格、括号建议在表头就规范掉不要留到代码里做替换。5. 二次开发把工具改成你项目里的样子5.1 自定义字段映射与类型覆盖默认的类型推断是通用规则但每个项目都有自己的约定。比如有的项目要求所有float字段保留两位小数有的要求枚举字段输出字符串而不是数字。改造思路是在Config/mapping.json里加一层覆盖规则{ skill.xlsx: { Id: int, Name: string, Cooldown: float2, Type: enum } }然后在TypeInferrer里读取这个配置优先按配置转换。float2表示保留两位小数的浮点数enum表示输出枚举名字符串。这样策划改表不用改代码你也不用每次手动调。5.2 接入 Unity 或后端服务的自动化流程工具跑通后下一步是把它塞进构建流程。Unity 项目里可以写一个Editor脚本在AssetPostprocessor里监听 Excel 文件变化自动调用这个转换工具把 JSON 输出到Resources或StreamingAssets。后端服务可以在 CI 里加一步dotnet run把配置表转成 JSON 后打包进 Docker 镜像。关键是把输入输出路径参数化不要硬编码。5.3 验证转换结果的一个小技巧改完代码后怎么确认没改坏我一般会准备一张“黄金测试表”里面覆盖各种边界空单元格、合并单元格、超长数字、中文、特殊字符、布尔值。每次改完跑一遍用diff对比输出 JSON 和预期结果。如果差异只在预期内就放心提交。这个习惯帮我省了很多次回滚。注意diff对比时记得忽略行尾空格和 BOM 差异否则会误报。5.4 从工具到库的演进如果团队里多个项目都要用可以把核心逻辑抽成 NuGet 包只暴露ExcelToJsonConverter.Convert(input, output, config)一个入口。这样其他项目引用包就行不用拷贝源码。抽包时注意把 EPPlus 和 Newtonsoft 的依赖标记为PrivateAssets避免版本冲突。从那以后我每次改转换规则都强制先跑一遍黄金测试表再对比 diff确认无误才提交。希望这个工具和这些踩坑记录能帮到你少加几次班。本文还有配套的精品资源点击获取
锦
锦皓数字建站
深耕本土企业品牌数字化升级,专注原创端正雅致商务官网,从视觉设计到稳定运维全程保驾护航。