C#中如何在XML后追加数据并读取XML?解决根级别数据错误
问题分析与解决方案
这问题我之前也碰到过,本质是XML解析器的规则限制——标准XML要求整个文档是合法的XML结构,你的文件在<Header>根节点结束后还有普通文本数据,这违反了XML的语法规范,所以XmlDocument.Load()会抛出“根级别数据无效”的错误。
你有两种选择:要么保留现有文件格式但换一种读取方式,要么重构文件格式让它符合标准XML规范。下面分别说明:
方案一:保留现有格式,拆分读取内容
如果必须兼容当前的“XML头部+普通数据”文件结构,可以把读取过程拆成两步:先读取并解析XML部分,再读取剩余的普通数据。
示例代码
private void readHeaderAndData(FileStream input, out string algorithm, out string remainingData) { algorithm = string.Empty; remainingData = string.Empty; // 先读取整个文件内容(如果文件过大,建议用逐行读取的方式优化内存占用) using (StreamReader reader = new StreamReader(input)) { string fullContent = reader.ReadToEnd(); // 定位XML根节点的结束位置 int headerEndPos = fullContent.IndexOf("</Header>"); if (headerEndPos != -1) { // 截取XML部分并解析 string xmlContent = fullContent.Substring(0, headerEndPos + "</Header>".Length); XmlDocument doc = new XmlDocument(); doc.LoadXml(xmlContent); algorithm = doc.SelectSingleNode("/Header/Algorithm")?.InnerText ?? string.Empty; // 截取剩余的普通数据 remainingData = fullContent.Substring(headerEndPos + "</Header>".Length).Trim(); } } }
注意事项
如果文件体积很大,ReadToEnd()会占用过多内存,此时建议用StreamReader.ReadLine()逐行读取,直到找到包含</Header>的行,再拼接前面的行作为XML内容,之后读取剩余行作为普通数据。
方案二:重构文件格式,将普通数据转为XML节点
如果可以修改文件的写入逻辑,最规范的做法是把普通数据放到XML节点中,让整个文件成为合法的XML文档。这样后续读取和维护都会更简单。
修改写入代码
private void setHeaderWithData(FileStream output, string nod2, string nod3, string fileData) { XmlDocument doc = new XmlDocument(); XmlNode docNode = doc.CreateXmlDeclaration("1.0", "UTF-8", "yes"); doc.AppendChild(docNode); // 创建唯一根节点(XML要求只能有一个根节点) XmlNode root = doc.CreateElement("Root"); doc.AppendChild(root); // 原有的Header节点 XmlNode header = doc.CreateElement("Header"); root.AppendChild(header); XmlNode algorithm = doc.CreateElement("Algorithm"); algorithm.InnerText = "alg"; header.AppendChild(algorithm); XmlNode node2 = doc.CreateElement("nod2"); node2.InnerText = nod2; header.AppendChild(node2); XmlNode node3 = doc.CreateElement("nod3"); node3.InnerText = nod3; header.AppendChild(node3); XmlNode node = doc.CreateElement("node"); header.AppendChild(node); XmlNode user1 = doc.CreateElement("User"); node.AppendChild(user1); XmlNode mail = doc.CreateElement("Email"); user1.AppendChild(mail); XmlNode sessionKey = doc.CreateElement("SessionKey"); user1.AppendChild(sessionKey); // 用CDATA包裹普通数据,避免特殊字符破坏XML结构 XmlNode dataNode = doc.CreateElement("FileData"); XmlCDataSection cdata = doc.CreateCDataSection(fileData); dataNode.AppendChild(cdata); root.AppendChild(dataNode); doc.Save(output); }
对应的读取代码
private void readHeaderAndData(FileStream input, out string algorithm, out string fileData) { algorithm = string.Empty; fileData = string.Empty; XmlDocument doc = new XmlDocument(); doc.Load(input); algorithm = doc.SelectSingleNode("/Root/Header/Algorithm")?.InnerText ?? string.Empty; fileData = doc.SelectSingleNode("/Root/FileData")?.InnerText ?? string.Empty; }
总结
- 如果需要兼容现有文件,优先选择方案一拆分读取;
- 如果可以控制文件生成逻辑,建议用方案二,因为符合XML标准规范,后续解析和维护更可靠。
内容的提问来源于stack exchange,提问作者Gutts
相关产品推荐
相关产品推荐

