C# 上位机开发 三大数据格式(Excel/XML/Json)导入导出完整方案
你需要的是 C# 上位机场景下 Excel、XML、Json 三种主流格式的全量数据持久化方案,以下内容兼顾无第三方依赖(XML/Json)和实战高效(Excel),适配 WinForm/WPF 上位机,可直接复用。
一、Json 数据导入导出(已优化,无第三方依赖)
基于.NET 自带System.Text.Json,无需 NuGet,适配.NET Core 3.0+/NET 5+,完美支持工控配置、采集数据场景。
1. 核心工具类
using System.IO;using System.Text;using System.Text.Json;/// <summary>/// Json 持久化工具类(System.Text.Json 版,无第三方依赖)/// </summary>public static class JsonPersistenceHelper{ // 全局序列化配置(解决工控场景中文、循环引用、null值问题) private static readonly JsonSerializerOptions _jsonOptions = new() { WriteIndented = true, // 格式化缩进,方便人工编辑 DefaultIgnoreCondition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingNull, ReferenceHandler = System.Text.Json.Serialization.ReferenceHandler.IgnoreCycles, // 忽略循环引用 PropertyNameCaseInsensitive = true, // 反序列化时大小写不敏感 Encoder = System.Text.Encodings.Web.JavascriptEncoder.UnsafeRelaxedJsonEscaping // 支持中文 }; /// <summary> /// Json 导出(序列化) /// </summary> public static bool ExportJson<T>(T data, string savePath) { try { string jsonStr = JsonSerializer.Serialize(data, _jsonOptions); // 不带BOM的UTF8编码,解决中文乱码 File.WriteAllText(savePath, jsonStr, new UTF8Encoding(false)); return true; } catch (Exception ex) { Console.WriteLine($"Json导出失败:{ex.Message}"); return false; } } /// <summary> /// Json 导入(反序列化) /// </summary> public static T? importJson<T>(string filePath) { try { if (!File.Exists(filePath)) return default; string jsonStr = File.ReadAllText(filePath, new UTF8Encoding(false)); return JsonSerializer.Deserialize<T>(jsonStr, _jsonOptions); } catch (Exception ex) { Console.WriteLine($"Json导入失败:{ex.Message}"); return default; } }}2. 调用示例(PLC 配置 / 采集数据通用)
// 沿用之前的PlcConfig/CollectData实体类,直接调用private void BtnJsonExport_Click(object sender, EventArgs e){ var config = new PlcConfig { IpAddress = "192.168.1.10", Port = 102, Rack = 0, Slot = 1 }; using (SaveFileDialog sfd = new SaveFileDialog()) { sfd.Filter = "Json文件|*.json|所有文件|*.*"; if (sfd.ShowDialog() == DialogResult.OK) { bool success = JsonPersistenceHelper.ExportJson(config, sfd.FileName); MessageBox.Show(success ? "Json导出成功" : "Json导出失败"); } }}private void BtnJsonimport_Click(object sender, EventArgs e){ using (OpenFileDialog ofd = new OpenFileDialog()) { ofd.Filter = "Json文件|*.json|所有文件|*.*"; if (ofd.ShowDialog() == DialogResult.OK) { PlcConfig? config = JsonPersistenceHelper.importJson<PlcConfig>(ofd.FileName); if (config != null) { // 赋值给界面控件 txtIp.Text = config.IpAddress; MessageBox.Show("Json导入成功"); } } }}二、XML 数据导入导出(无第三方依赖,.NET 自带)
XML 格式适用于结构化配置、标准化数据交互(如工控系统间协议传输),基于System.Xml.Serialization实现,无需额外引用。
1. 核心工具类
using System.IO;using System.Text;using System.Xml.Serialization;/// <summary>/// XML 持久化工具类(.NET自带,无第三方依赖)/// </summary>public static class XmlPersistenceHelper{ /// <summary> /// XML 导出(序列化) /// </summary> public static bool ExportXml<T>(T data, string savePath) { try { // XmlSerializer 需指定泛型类型 XmlSerializer serializer = new XmlSerializer(typeof(T)); // 使用UTF8编码,解决中文乱码 using (StreamWriter sw = new StreamWriter(savePath, false, new UTF8Encoding(false))) { // 格式化XML,方便查看 serializer.Serialize(sw, data); } return true; } catch (Exception ex) { Console.WriteLine($"XML导出失败:{ex.Message}"); return false; } } /// <summary> /// XML 导入(反序列化) /// </summary> public static T? importXml<T>(string filePath) { try { if (!File.Exists(filePath)) return default; XmlSerializer serializer = new XmlSerializer(typeof(T)); using (StreamReader sr = new StreamReader(filePath, new UTF8Encoding(false))) { object? result = serializer.Deserialize(sr); return result is T ? (T)result : default; } } catch (Exception ex) { Console.WriteLine($"XML导入失败:{ex.Message}"); return default; } }}2. 调用示例(配置 / 批量数据通用)
// 导出XML(PLC配置)private void BtnXmlExport_Click(object sender, EventArgs e){ var config = new PlcConfig { IpAddress = "192.168.1.10", Port = 102, MonitorTags = new List<string> { "DB1.DBW0", "DB1.DBW2" } }; using (SaveFileDialog sfd = new SaveFileDialog()) { sfd.Filter = "XML文件|*.xml|所有文件|*.*"; sfd.Title = "导出PLC配置(XML)"; if (sfd.ShowDialog() == DialogResult.OK) { bool success = XmlPersistenceHelper.ExportXml(config, sfd.FileName); MessageBox.Show(success ? "XML导出成功" : "XML导出失败"); } }}// 导入XML(PLC配置)private void BtnXmlimport_Click(object sender, EventArgs e){ using (OpenFileDialog ofd = new OpenFileDialog()) { ofd.Filter = "XML文件|*.xml|所有文件|*.*"; ofd.Title = "导入PLC配置(XML)"; if (ofd.ShowDialog() == DialogResult.OK) { PlcConfig? config = XmlPersistenceHelper.importXml<PlcConfig>(ofd.FileName); if (config != null) { txtIp.Text = config.IpAddress; txtPort.Text = config.Port.ToString(); MessageBox.Show("XML导入成功"); } else { MessageBox.Show("XML导入失败或文件无效"); } } }}// 批量导出采集数据(XML)private void BtnXmlExportCollect_Click(object sender, EventArgs e){ List<CollectData> dataList = new List<CollectData>() { new CollectData{CollectTime=DateTime.Now, StationNo="ST01", Value=12.3m, IsQualified=true}, new CollectData{CollectTime=DateTime.Now, StationNo="ST02", Value=11.8m, IsQualified=true} }; using (SaveFileDialog sfd = new SaveFileDialog()) { sfd.Filter = "XML文件|*.xml|所有文件|*.*"; if (sfd.ShowDialog() == DialogResult.OK) { bool success = XmlPersistenceHelper.ExportXml(dataList, sfd.FileName); MessageBox.Show(success ? "采集数据XML批量导出成功" : "导出失败"); } }}三、Excel 数据导入导出(实战高效,NPOI 版)
Excel 是上位机报表导出、批量数据交互的主流格式,优先使用NPOI(免费开源,支持.xls/.xlsx,无需安装 Office)。
1. 前置准备:安装 NPOI
通过 NuGet 安装:搜索NPOI,安装最新稳定版(支持.NET framework/.NET Core)。
2. 核心工具类(支持.xlsx,兼容批量数据)
using NPOI.SS.UserModel;using NPOI.XSSF.UserModel;using System.IO;using System.Text;/// <summary>/// Excel 持久化工具类(NPOI 版,支持.xlsx)/// </summary>public static class ExcelPersistenceHelper{ /// <summary> /// Excel 导出(批量数据适配) /// </summary> /// <typeparam name="T">数据实体类型</typeparam> /// <param name="dataList">数据列表</param> /// <param name="savePath">保存路径</param> /// <param name="headerDict">表头字典(键:实体属性名,值:表头显示名)</param> public static bool ExportExcel<T>(List<T> dataList, string savePath, Dictionary<string, string> headerDict) { try { // 创建Excel工作簿(.xlsx格式) IWorkbook workbook = new XSSFWorkbook(); ISheet sheet = workbook.CreateSheet("上位机数据"); // 工作表名称 // 1. 创建表头行 IRow headerRow = sheet.CreateRow(0); int colIndex = 0; // 存储属性名列表,用于后续赋值 List<string> propertyNames = new List<string>(); foreach (var kv in headerDict) { headerRow.CreateCell(colIndex).SetCellValue(kv.Value); propertyNames.Add(kv.Key); // 自动调整列宽 sheet.AutoSizeColumn(colIndex); colIndex++; } // 2. 填充数据行 int rowIndex = 1; foreach (var data in dataList) { IRow dataRow = sheet.CreateRow(rowIndex); colIndex = 0; // 反射获取实体属性值 var properties = typeof(T).GetProperties(); foreach (var propName in propertyNames) { var prop = properties.FirstOrDefault(p => p.Name == propName); if (prop != null) { object? value = prop.GetValue(data); // 根据类型设置单元格值 if (value != null) { if (value is DateTime datevalue) { dataRow.CreateCell(colIndex).SetCellValue(datevalue.ToString("yyyy-MM-dd HH:mm:ss")); } else if (value is int || value is decimal || value is double) { dataRow.CreateCell(colIndex).SetCellValue(Convert.ToDouble(value)); } else if (value is bool boolValue) { dataRow.CreateCell(colIndex).SetCellValue(boolValue ? "是" : "否"); } else { dataRow.CreateCell(colIndex).SetCellValue(value.ToString()); } } else { dataRow.CreateCell(colIndex).SetCellValue(""); } } sheet.AutoSizeColumn(colIndex); colIndex++; } rowIndex++; } // 3. 写入文件 using (FileStream fs = new FileStream(savePath, FileMode.Create, FileAccess.Write)) { workbook.Write(fs); } workbook.Close(); return true; } catch (Exception ex) { Console.WriteLine($"Excel导出失败:{ex.Message}"); return false; } } /// <summary> /// Excel 导入(批量数据适配) /// </summary> /// <typeparam name="T">数据实体类型</typeparam> /// <param name="filePath">文件路径</param> /// <param name="headerDict">表头字典(键:实体属性名,值:表头显示名)</param> public static List<T>? importExcel<T>(string filePath, Dictionary<string, string> headerDict) where T : new() { try { if (!File.Exists(filePath)) return null; List<T> dataList = new List<T>(); // 读取Excel文件 using (FileStream fs = new FileStream(filePath, FileMode.Open, FileAccess.Read)) { IWorkbook workbook = new XSSFWorkbook(fs); ISheet sheet = workbook.GetSheetAt(0); // 获取第一个工作表 if (sheet == null) return null; // 1. 验证表头(可选,提高容错性) IRow headerRow = sheet.GetRow(0); if (headerRow == null) return null; // 表头显示名列表 List<string> headerNames = headerDict.Values.ToList(); for (int i = 0; i < headerNames.Count; i++) { ICell cell = headerRow.GetCell(i); if (cell == null || cell.StringCellValue != headerNames[i]) { Console.WriteLine("Excel表头不匹配,导入失败"); return null; } } // 2. 读取数据行 var properties = typeof(T).GetProperties(); // 表头对应属性名 List<string> propNames = headerDict.Keys.ToList(); for (int rowIndex = 1; rowIndex <= sheet.LastRowNum; rowIndex++) { IRow dataRow = sheet.GetRow(rowIndex); if (dataRow == null) continue; T data = new T(); for (int colIndex = 0; colIndex < propNames.Count; colIndex++) { string propName = propNames[colIndex]; var prop = properties.FirstOrDefault(p => p.Name == propName); if (prop == null) continue; ICell cell = dataRow.GetCell(colIndex); if (cell == null) continue; // 根据属性类型赋值 Type propType = prop.PropertyType; // 处理可空类型 propType = Nullable.GetUnderlyingType(propType) ?? propType; object? cellValue = null; switch (cell.CellType) { case CellType.String: cellValue = cell.StringCellValue; break; case CellType.Numeric: if (DateUtil.IsCellDateFormatted(cell)) { cellValue = cell.DateCellValue; } else { cellValue = cell.NumericCellValue; } break; case CellType.Boolean: cellValue = cell.BooleanCellValue; break; default: cellValue = null; break; } // 类型转换并赋值 if (cellValue != null && !string.IsNullOrEmpty(cellValue.ToString())) { try { cellValue = Convert.ChangeType(cellValue, propType); prop.SetValue(data, cellValue); } catch { // 类型转换失败则跳过该字段 continue; } } } dataList.Add(data); } } return dataList; } catch (Exception ex) { Console.WriteLine($"Excel导入失败:{ex.Message}"); return null; } }}3. 调用示例(采集数据批量导出 / 导入)
// 导出Excel(采集记录)private void BtnExcelExport_Click(object sender, EventArgs e){ // 模拟采集数据 List<CollectData> dataList = new List<CollectData>() { new CollectData{CollectTime=DateTime.Now, StationNo="ST01", Value=12.3m, IsQualified=true, Remark="正常"}, new CollectData{CollectTime=DateTime.Now, StationNo="ST02", Value=11.8m, IsQualified=true, Remark="正常"}, new CollectData{CollectTime=DateTime.Now, StationNo="ST03", Value=15.6m, IsQualified=false, Remark="超上限"} }; // 定义表头(键:实体属性名,值:Excel表头显示名) Dictionary<string, string> headerDict = new Dictionary<string, string>() { { "CollectTime", "采集时间" }, { "StationNo", "工位号" }, { "Value", "采集值" }, { "IsQualified", "是否合格" }, { "Remark", "备注" } }; using (SaveFileDialog sfd = new SaveFileDialog()) { sfd.Filter = "Excel文件(*.xlsx)|*.xlsx|所有文件|*.*"; sfd.Title = "批量导出采集记录(Excel)"; if (sfd.ShowDialog() == DialogResult.OK) { bool success = ExcelPersistenceHelper.ExportExcel(dataList, sfd.FileName, headerDict); MessageBox.Show(success ? $"Excel导出成功,共{dataList.Count}条记录" : "Excel导出失败"); } }}// 导入Excel(采集记录)private void BtnExcelimport_Click(object sender, EventArgs e){ // 表头字典(需与导出时一致) Dictionary<string, string> headerDict = new Dictionary<string, string>() { { "CollectTime", "采集时间" }, { "StationNo", "工位号" }, { "Value", "采集值" }, { "IsQualified", "是否合格" }, { "Remark", "备注" } }; using (OpenFileDialog ofd = new OpenFileDialog()) { ofd.Filter = "Excel文件(*.xlsx)|*.xlsx|所有文件|*.*"; ofd.Title = "批量导入采集记录(Excel)"; if (ofd.ShowDialog() == DialogResult.OK) { List<CollectData>? dataList = ExcelPersistenceHelper.importExcel<CollectData>(ofd.FileName, headerDict); if (dataList != null && dataList.Count > 0) { // 绑定到DataGridView显示 dgvCollect.DataSource = dataList; MessageBox.Show($"Excel导入成功,共{dataList.Count}条记录"); } else { MessageBox.Show("Excel导入失败或文件无有效数据"); } } }}四、三种格式适用场景对比(上位机开发参考)
数据格式 | 核心优势 | 适用场景 | 依赖情况 |
Json | 轻量、简洁、易解析、兼容性强 | PLC 配置、机器人参数、轻量采集记录、跨系统接口 | 无(.NET 自带) |
XML | 结构化强、可扩展、支持命名空间、标准化 | 工控协议交互、复杂配置文件、需严格格式校验的场景 | 无(.NET 自带) |
Excel | 可视化强、支持公式 / 格式、用户易编辑 | 生产报表、批量数据导入导出、人工核对数据 | NPOI(免费开源) |
五、上位机开发避坑要点
- 编码统一:三种格式均使用UTF8编码(Excel 通过 NPOI 默认支持,Json/XML 显式指定),避免工控电脑中文乱码;
- 异常处理:导入导出时增加try-catch,并通过MessageBox/ 日志提示用户,提高上位机稳定性;
- 文件对话框:统一使用SaveFileDialog(导出)/OpenFileDialog(导入),符合上位机操作习惯;
- 大文件处理:Excel/Json 大文件(>100MB)建议分块导出,避免内存溢出;XML 不适合超大文件存储。
总结
- Json/XML 基于.NET 自带类库,无需第三方依赖,优先用于配置存储和标准化数据交互;
- Excel 使用 NPOI 实现,支持可视化报表,是批量数据人工交互的最优选择;
- 三个工具类均封装为静态类,调用简洁,可直接集成到 WinForm/WPF 上位机项目中;
- 配套调用示例覆盖工控常用场景(PLC 配置、采集记录),无需大幅修改即可复用。

