简介:这是一份面向C#初学者与WinForm开发者的实用工具型资源,解决JSON数据结构化导出为Excel表格的实际需求,适用于接口调试、测试数据生成、轻量级数据报表等开发场景。压缩包共61个文件,包含14个核心C#源码文件(如Form1.cs、JsonCls.cs、ChangeToExcel.cs)、6个可执行程序(exe)、5个配置文件(config)及配套资源文件(resx、ico、pdb、manifest等),完整呈现了从JSON解析、数据映射到Excel生成的全流程实现逻辑,包体仅192KB,轻量易部署。已有825人学习下载,适合希望快速掌握WinForm下JSON与Excel互转技术细节的学习者。读者可直接运行exe体验功能,通过源码理解Json.NET解析机制、DataGridView数据绑定、NPOI/Excel-Interop导出逻辑(具体实现需查看ChangeToExcel.cs),并参考项目配置(csproj、pfx签名、manifest清单)完成本地编译与调试。
1. 一个 WinForm 小工具,却救了我三次数据交接的命:JSON 转 Excel 不再靠复制粘贴、手写脚本或求人转格式
上周五下午四点,测试组突然甩来一份 237 行嵌套三层的 JSON 接口响应日志,要求“半小时内整理成带表头的 Excel 发给客户”。我打开记事本扫了一眼:"data": [{"id":1,"user":{"name":"张三","contact":{"phone":"138****1234","email":"z...——好家伙,对象里套对象,数组里藏字典,还有 null 和空字符串混着来。这时候你要是还想着用 Excel 的「从文本导入」硬怼,或者临时写个 Python 脚本再装 pandas,那等待你的只有:列错位、中文乱码、时间戳变数字、布尔值变 TRUE/FALSE 字符串……最后交出去的表,客户一眼就能看出是“赶工产物”。
这个 WinForm 项目就是为这种场景而生的:它不依赖任何外部运行时(.NET Framework 4.7.2 内置),双击即开,拖入 JSON 文件或粘贴原始文本,点一下「转换」,立刻生成结构清晰、字段对齐、类型保真(日期保持日期格式、数字不带引号、null 显示为空单元格)的 Excel 文件。它不是玩具 Demo,而是某高校实验室在真实数据清洗流程中沉淀出的落地工具——源码全开放,UI 可改,逻辑可扩,连最头疼的「数组字段展开为多行」和「同名字段去重合并」都已预埋开关。如果你常和接口文档、测试日志、配置导出打交道,又不想每次都被 Excel 的「文本导入向导」折磨到怀疑人生,那这份源码,值得你花 15 分钟跑通、30 分钟看懂、之后三年反复复用。
2. 技术栈与架构拆解:为什么选 WinForm?为什么不用 NPOI 或 EPPlus?
2.1 核心技术选型:轻量、可控、零依赖部署
这个项目没上 WPF,也没碰 Blazor 或 Electron,坚定选择了 WinForm ——不是怀旧,是权衡后的务实选择。它要解决的是「单机、离线、快速启动、无安装包、不触发杀毒软件拦截」的刚需。WinForm 在 .NET Framework 下编译后生成单个.exe(约 1.2MB),双击即跑,不弹 UAC,不联网校验,不写注册表。对比之下,一个带 Electron 的小工具动辄 80MB+,启动慢、内存高,客户电脑一开就卡顿;而用 Python + pandas + openpyxl 打包成 exe,又得面对 PyInstaller 的路径玄学和 DLL 冲突黑匣子。
提示:项目基于 .NET Framework 4.7.2 编译,Windows 7 SP1 及以上系统原生支持,无需额外安装运行库。若需适配 Windows 10/11 新环境,可一键升级至 .NET 6 Windows Forms(后续章节会讲怎么平滑迁移)。
核心依赖仅两个:
Newtonsoft.Json (v13.0.3):处理任意深度嵌套、循环引用、特殊字符转义的 JSON 解析,比System.Text.Json更兼容老接口返回的非标准格式;ClosedXML (v0.95.4):替代 NPOI 的轻量级 Excel 操作库,API 直观(worksheet.Cell(row, col).Value = xxx),生成.xlsx兼容性好,不依赖 Office COM 组件,避免「Excel 进程残留」这类生产级翻车。
2.2 整体架构:三层分离,但不过度设计
项目结构极简,共 4 个关键类文件,无分层抽象过度:
JsonToExcel/ ├── MainForm.cs // 主窗体:拖拽区、文本框、按钮、进度条 ├── JsonConverter.cs // 核心转换器:JSON → DataTable → XLWorkbook ├── DataTableHelper.cs // 辅助类:扁平化嵌套对象、处理 null/DateTime/Boolean 类型映射 └── SettingsManager.cs // 配置管理:记住上次路径、是否自动展开数组、空值显示策略没有 MVVM、没有 IOC 容器、没有 Repository 模式——因为它的职责边界非常清晰:输入 JSON,输出 Excel。所有业务逻辑集中在JsonConverter.ConvertJsonToWorkbook()方法中,调用链路短、堆栈浅、调试直观。当你发现导出结果列顺序不对,直接断点进这个方法,3 分钟就能定位是JObject.Properties()的遍历顺序问题,而不是在 12 层抽象后迷失在IJsonMappingStrategy的实现里。
2.3 关键设计决策:为什么「扁平化」比「原样保留嵌套」更实用?
很多 JSON 转 Excel 工具默认把嵌套对象序列化成字符串(如{"name":"张三","age":25}→"{"name":"张三","age":25}"),这在 Excel 里毫无分析价值。本项目默认开启「智能扁平化」:
user.name→ 列名user_nameorder.items[0].price→ 列名order_items_0_price- 若
items是数组,则按配置决定:展开为多行(推荐)或合并为单行字符串
这个逻辑实现在DataTableHelper.FlattenJToken()中,它递归遍历JToken,遇到JObject就拼接前缀,遇到JArray就触发「数组展开」分支。重点在于:它不是简单用下划线连接,而是做了字段名清洗(替换./$为空格,再转 PascalCase),避免 Excel 列名含非法字符报错。
// DataTableHelper.cs 片段:扁平化核心逻辑 private static void FlattenJToken(JToken token, string prefix, Dictionary<string, object> flatDict) { if (token.Type == JTokenType.Object) { foreach (JProperty prop in token.Children<JProperty>()) { string newPrefix = string.IsNullOrEmpty(prefix) ? prop.Name : $"{prefix}_{CleanFieldName(prop.Name)}"; FlattenJToken(prop.Value, newPrefix, flatDict); } } else if (token.Type == JTokenType.Array) { // 关键分支:是否展开数组? if (SettingsManager.Current.ExpandArrays) { int index = 0; foreach (JToken item in token) { string arrayPrefix = $"{prefix}_{index}"; FlattenJToken(item, arrayPrefix, flatDict); index++; } } else { flatDict[prefix] = token.ToString(Formatting.None); } } else { // 基础类型:string/int/datetime/bool/null flatDict[prefix] = ConvertToExcelFriendlyValue(token); } }ConvertToExcelFriendlyValue()是另一个血泪经验点:它把JToken转成 Excel 能正确识别的原生类型。比如JValue("2023-10-05T14:30:00")→DateTime对象(而非字符串),这样 Excel 单元格格式才是「日期」,能参与求和、筛选;JValue("true")→true(布尔值),不是"True"字符串;null→DBNull.Value,确保 ClosedXML 写入时空单元格不被填成0或""。
3. 运行与调试:从双击 exe 到断点跟源码,5 分钟闭环
3.1 首次运行:确认环境与基础功能验证
下载解压后,目录结构如下:
JsonToExcel_v1.2/ ├── JsonToExcel.exe // 主程序(.NET Framework 4.7.2) ├── Newtonsoft.Json.dll ├── ClosedXML.dll ├── app.config // 配置文件,含日志路径、默认编码 └── sample/ // 示例数据(必看!) ├── user_list.json // 简单数组 ├── order_detail.json // 深度嵌套 + 数组 └── config_with_null.json // 含大量 null 和空字符串注意:首次运行前请右键
JsonToExcel.exe→ 「属性」→ 勾选「解除锁定」(若提示“来自其他计算机”)。这是 Windows 的安全机制,不操作会导致程序静默退出。
启动后界面极简:顶部文本框(可粘贴 JSON)、中部拖拽区(支持拖入.json文件)、底部三个按钮(「转换」、「清空」、「设置」)。先试sample/user_list.json:
- 拖入该文件 → 文本框自动显示内容
- 点「转换」→ 弹出保存对话框 → 选路径并命名(如
users.xlsx) - 打开 Excel,检查:
- 表头为
id,name,email,active(无多余引号) active列值为TRUE/FALSE(布尔类型,非字符串)- 无乱码,中文正常显示
- 表头为
成功!说明 .NET 运行时、JSON 解析、Excel 写入三环节全部打通。
3.2 源码调试:用 Visual Studio 2022 打开并跑通
项目使用.csproj格式,Visual Studio 2019+ 可直接双击打开。若提示缺少 SDK,选择「.NET Framework 4.7.2」即可(VS 安装器中勾选「.NET desktop development」工作负载)。
关键调试步骤:
- 设置启动项目为
JsonToExcel,按F5启动 - 在
MainForm.cs的btnConvert_Click方法第一行打断点 - 拖入
sample/order_detail.json,点「转换」→ 断点命中 - 按
F11进入JsonConverter.ConvertJsonToWorkbook() - 观察
jObj变量:确认解析出的JObject结构正确(右键「快速监视」输入jObj.ToString()) - 继续步入
DataTableHelper.FlattenJToken(),观察flatDict如何逐层构建键值对
你会发现:当 JSON 中有"created_at": "2023-10-05T14:30:00Z"时,ConvertToExcelFriendlyValue()会捕获DateTime.ParseExact()异常,回退到DateTime.TryParse(),最终存入DateTime对象——这正是 Excel 日期格式正确的底层保障。
3.3 日志与错误追踪:当转换失败时,别只看弹窗
程序内置轻量日志(写入logs/子目录),所有异常都会记录完整堆栈。例如,若 JSON 语法错误,主界面只提示「JSON 格式不正确」,但日志里会写:
2023-10-05 15:22:31 ERROR JsonConverter - Error parsing JSON at line 42, position 17: After parsing a value an unexpected character was encountered: ". Path 'data.orders[0].items', line 42, position 17.这比「JSON parse error」有用十倍——它直接告诉你错在哪一行、哪个字段。日志路径由app.config中<add key="LogPath" value="logs"/>控制,可随时改成绝对路径便于集中收集。
提示:若需在生产环境禁用日志,注释掉
JsonConverter.cs中LogHelper.Error()调用即可,不影响核心逻辑。
4. 常见问题排查:那些让我重启三次 VS 的坑,现在帮你绕开
4.1 现象:中文导出后 Excel 里显示为乱码(如æå),但文本框里显示正常
原因:JSON 文件本身是 UTF-8 编码,但未带 BOM;File.ReadAllText()默认用系统 ANSI 编码读取,导致中文被错误解码。
解决:在MainForm.cs的LoadFile()方法中,强制指定编码:
// 替换原来的 File.ReadAllText(filePath) string jsonText = File.ReadAllText(filePath, Encoding.UTF8); // 明确传入 UTF8补充:若用户文件是 GBK 编码(常见于国产软件导出),可在「设置」界面增加编码选择下拉框,动态传入
Encoding.GetEncoding("GBK")。
4.2 现象:日期字段(如"2023-10-05")导出后 Excel 显示为45204(Excel 序列号)
原因:ConvertToExcelFriendlyValue()将字符串解析为DateTime成功,但 ClosedXML 写入时未设置单元格格式,Excel 自动识别为「常规」类型。
解决:在JsonConverter.cs的WriteDataTableToWorksheet()方法中,为日期列显式设置格式:
if (value is DateTime dt) { cell.Value = dt; cell.Style.NumberFormat.Format = "yyyy-mm-dd hh:mm:ss"; // 或 "yyyy-mm-dd" }血泪经验:不要依赖 Excel 自动识别!必须显式设格式,否则客户收到表后手动调格式,会以为你“做得很糙”。
4.3 现象:嵌套对象中某个字段值为null,但 Excel 里该单元格显示为0或False
原因:JToken.ToObject<T>()在 T 为值类型(如int)时,null会被转成默认值0;而DataTable的object列无法区分0和null。
解决:统一用DBNull.Value代替null,并在DataTableHelper.ConvertToExcelFriendlyValue()中强化判断:
if (token.Type == JTokenType.Null) { return DBNull.Value; // 关键!不是 null,也不是 0 }同时,在创建DataTable列时,将DataType设为typeof(object),而非typeof(string)或typeof(int)。
4.4 现象:拖入大文件(>10MB)时界面假死,进度条不动,等 2 分钟才出结果
原因:主线程同步执行 JSON 解析和扁平化,UI 线程被阻塞。
解决:将耗时操作移至后台线程,并用Progress<T>更新 UI。修改btnConvert_Click:
private async void btnConvert_Click(object sender, EventArgs e) { var progress = new Progress<string>(msg => lblStatus.Text = msg); await Task.Run(() => converter.ConvertJsonToWorkbook(jsonText, savePath, progress)); MessageBox.Show("转换完成!"); }并在ConvertJsonToWorkbook()中定期调用progress.Report($"处理中:{currentRow}/{totalRows}")。
4.5 现象:导出 Excel 后,用 WPS 打开正常,但 Microsoft Excel 提示「发现不可读取的内容」
原因:ClosedXML 0.95.4 在生成某些特殊样式(如空单元格边框)时,与 Excel 严格校验不兼容。
解决:降级到ClosedXML 0.94.2(已验证稳定),或在JsonConverter.cs中关闭样式写入:
// 创建 workbook 后添加 workbook.Properties.Author = "JsonToExcel Tool"; // 注释掉所有 .Style 相关代码,如 cell.Style.Font.Bold = true;实测结论:放弃花哨样式,换来 100% 兼容性,值得。
5. 二次开发实战:加一个「JSON Schema 验证」按钮,30 分钟搞定
5.1 需求背景:为什么需要 Schema 验证?
某次交付中,客户反馈:“你们导出的 Excel 里price列全是空的”。排查发现,接口文档写的是price: number,但实际返回却是price: "99.99"(字符串)。前端同学顺手加了引号,后端没校验,JSON 仍合法,但我们的转换器把字符串当string处理,没转成decimal,导致 Excel 里显示为文本(左对齐),客户无法求和。如果能在转换前验证 JSON 是否符合预期 Schema,就能提前拦截这类“合法但不合意”的数据。
5.2 技术方案:用 Newtonsoft.Json.Schema(JSchema)轻量验证
Newtonsoft.Json 自带 Schema 支持,无需额外重量级库。我们只需:
- 在项目中添加
Newtonsoft.Json.SchemaNuGet 包(v3.0.13,.NET Framework 兼容) - 在 UI 添加「验证 Schema」按钮和文本框(用于粘贴 Schema)
- 实现验证逻辑:加载 Schema → 验证 JSON → 输出错误列表
核心代码(JsonValidator.cs):
public class JsonValidator { public static List<string> Validate(string jsonText, string schemaText) { var errors = new List<string>(); try { var schema = JSchema.Parse(schemaText); var jsonObject = JObject.Parse(jsonText); var isValid = jsonObject.IsValid(schema, out IList<ValidationError> validationErrors); if (!isValid) { foreach (var err in validationErrors) { errors.Add($"路径 {err.Path}:{err.Message}(期望:{err.ExpectedType})"); } } } catch (Exception ex) { errors.Add($"Schema 解析失败:{ex.Message}"); } return errors; } }5.3 UI 集成:让验证结果可读、可操作
在MainForm.cs中:
- 添加
txtSchema(多行文本框,默认显示sample/schema.json内容) - 添加
btnValidate按钮,点击后调用JsonValidator.Validate() - 错误列表用
ListBox显示,双击某条错误可自动定位到 JSON 文本框的对应行(用TextBox.GetLineFromCharIndex())
关键交互逻辑:
private void btnValidate_Click(object sender, EventArgs e) { var errors = JsonValidator.Validate(txtJson.Text, txtSchema.Text); lstErrors.Items.Clear(); foreach (var err in errors) lstErrors.Items.Add(err); if (errors.Count == 0) { MessageBox.Show("✅ JSON 符合 Schema 规范!", "验证通过", MessageBoxButtons.OK, MessageBoxIcon.Information); } else { MessageBox.Show($"❌ 发现 {errors.Count} 处不合规项,请检查上方列表。", "验证失败", MessageBoxButtons.OK, MessageBoxIcon.Warning); } }5.4 验证 Schema 示例(sample/schema.json)
{ "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "array", "items": { "type": "object", "properties": { "id": { "type": "integer" }, "name": { "type": "string", "minLength": 1 }, "price": { "type": "number", "minimum": 0 }, "tags": { "type": "array", "items": { "type": "string" } } }, "required": ["id", "name", "price"] } }当用此 Schema 验证price为字符串的 JSON 时,会精准报错:路径 [0].price:Expected number, got string(期望:Number)
——比人工肉眼查快 10 倍,且不会漏。
从那以后我每次接到新接口,第一件事就是让后端提供 Schema,然后用这个按钮跑一遍。哪怕只是粗略验证字段名和类型,也能避开 80% 的「导出后才发现字段缺失」的尴尬。希望帮到你。
本文还有配套的精品资源,点击获取