如何用ExcelDataReader读取Excel数据匹配文件名更新XML节点?
刚好之前做过类似的需求,用ExcelDataReader结合LINQ to XML就能搞定,我给你一步步拆解实现方法:
实现XML文件匹配XLSX字段并更新节点的方法(使用ExcelDataReader)
一、准备工作:安装依赖包
首先需要安装两个NuGet包,分别用于读取Excel和操作XML:
- 读取Excel的核心包:
ExcelDataReader - 辅助将Excel数据转为DataSet的包:
ExcelDataReader.DataSet - .NET自带的
System.Xml.Linq(一般无需额外安装,默认包含在项目中)
可以通过NuGet包管理器安装,或者用.NET CLI命令:
dotnet add package ExcelDataReader dotnet add package ExcelDataReader.DataSet
二、核心实现步骤
1. 读取XLSX数据,构建文件名匹配字典
先把XLSX里的文件名、DOI、Publisher数据读出来,存成字典(文件名作为键),这样后续匹配XML文件时能快速查找对应数据:
using ExcelDataReader; using System.Data; using System.IO; using System.Xml.Linq; using System.Collections.Generic; // 第一步:读取XLSX并构建匹配字典 var xlsxFilePath = @"C:\你的路径\数据文件.xlsx"; var xmlDataMap = new Dictionary<string, (string Doi, string Publisher)>(); // 注册编码(处理旧版Excel的编码问题,必须加这行) System.Text.Encoding.RegisterProvider(System.Text.CodePagesEncodingProvider.Instance); using (var stream = File.Open(xlsxFilePath, FileMode.Open, FileAccess.Read)) { using (var reader = ExcelReaderFactory.CreateReader(stream)) { // 配置读取规则:使用第一行作为表头 var dataSet = reader.AsDataSet(new ExcelDataSetConfiguration() { ConfigureDataTable = (_) => new ExcelDataTableConfiguration() { UseHeaderRow = true } }); // 假设数据在第一个工作表 DataTable dataTable = dataSet.Tables[0]; foreach (DataRow row in dataTable.Rows) { // 注意:这里要确保XLSX里的文件名和XML文件名完全匹配(包括后缀) // 如果XLSX里没有.xml后缀,就手动加上:fileName = $"{row["FileName"].ToString().Trim()}.xml" string fileName = row["FileName"].ToString().Trim(); string doi = row["DOI"].ToString().Trim(); string publisher = row["Publisher"].ToString().Trim(); if (!string.IsNullOrEmpty(fileName)) { xmlDataMap[fileName] = (doi, publisher); } } } }
2. 遍历XML文件,匹配并更新节点
遍历指定文件夹下的所有XML文件,根据文件名从字典中取对应数据,更新或创建目标节点:
// 第二步:遍历XML文件夹,更新节点 var xmlFolderPath = @"C:\你的路径\XML文件夹"; foreach (var xmlFilePath in Directory.GetFiles(xmlFolderPath, "*.xml")) { string xmlFileName = Path.GetFileName(xmlFilePath); if (xmlDataMap.TryGetValue(xmlFileName, out var targetData)) { XDocument xmlDoc = XDocument.Load(xmlFilePath); // 处理XML命名空间(如果你的XML有默认命名空间,必须加这行,否则找不到节点) XNamespace defaultNs = xmlDoc.Root?.GetDefaultNamespace() ?? ""; // 处理<publisher-name>节点:存在则更新,不存在则创建 var publisherNode = xmlDoc.Descendants(defaultNs + "publisher-name").FirstOrDefault(); if (publisherNode != null) { publisherNode.Value = targetData.Publisher; } else { // 这里假设要把节点添加到<front>下,你需要根据自己的XML结构调整父节点 var frontNode = xmlDoc.Descendants(defaultNs + "front").FirstOrDefault(); frontNode?.Add(new XElement(defaultNs + "publisher-name", targetData.Publisher)); } // 处理<article-id pub-id-type="doi">节点:存在则更新,不存在则创建 var doiNode = xmlDoc.Descendants(defaultNs + "article-id") .FirstOrDefault(n => n.Attribute("pub-id-type")?.Value == "doi"); if (doiNode != null) { doiNode.Value = targetData.Doi; } else { // 这里假设要把节点添加到<article-meta>下,根据实际XML结构调整 var articleMetaNode = xmlDoc.Descendants(defaultNs + "article-meta").FirstOrDefault(); articleMetaNode?.Add(new XElement(defaultNs + "article-id", targetData.Doi) { Attributes = { new XAttribute("pub-id-type", "doi") } }); } // 保存修改后的XML文件 xmlDoc.Save(xmlFilePath); Console.WriteLine($"已成功更新:{xmlFileName}"); } else { Console.WriteLine($"未找到匹配数据,跳过:{xmlFileName}"); } }
三、关键注意事项
- 命名空间处理:如果你的XML文件有默认命名空间,必须用
XNamespace来定位节点,否则会出现“找不到节点”的问题。 - 文件名匹配:确保XLSX中的文件名和XML文件名完全一致(包括后缀、大小写,Windows下不区分大小写,但Linux/macOS区分),不一致的话要提前做格式转换。
- 节点位置调整:代码中假设了节点的父节点(比如
<front>、<article-meta>),你需要根据自己的XML实际结构修改这些父节点的定位逻辑。 - 异常处理:实际使用时建议添加
try-catch块,处理文件被占用、XML格式错误等异常情况。 - 空值处理:如果XLSX中DOI或Publisher为空,代码会写入空字符串,你可以根据需求添加判断,比如空值时不更新节点。
内容的提问来源于stack exchange,提问作者Don_B
相关产品推荐
相关产品推荐

