XmlDocument.Load() 抛异常主因是路径错误或编码不匹配,如UTF-8带BOM被误判为UTF-16;查不到节点多因未处理默认命名空间;Save变空常因文件被占用;大文件应优先用XmlReader而非DOM。

XmlDocument.Load() 为什么抛出 XmlException?
多数报错不是因为 XML 写得“不标准”,而是加载路径或编码没对上。比如用 XmlDocument.Load("config.xml") 加载带 BOM 的 UTF-8 文件,.NET Framework 会误判为 UTF-16;或者文件路径是相对路径,但当前工作目录不是你预期的目录。
- 优先用
XmlDocument.Load(XmlReader.Create(path, new XmlReaderSettings { DtdProcessing = DtdProcessing.Ignore })),显式禁用 DTD 解析,避免网络外连或 XXE 风险 - 如果文件来自用户上传或不确定编码,先用
File.ReadAllBytes()读字节,再用new MemoryStream(bytes)+XmlReader.Create(),绕过自动编码探测 - 别依赖异常消息里的行号——它常指向 XML 声明之后的第一行,实际问题可能在
<?xml version="1.0" encoding="GBK"?>这种声明本身
用 SelectNodes() 查不到节点?XPath 表达式写对了吗
XPath 在 XmlDocument 里默认不支持命名空间前缀,也不区分大小写,但对文档结构敏感。常见查不到,90% 是因为根节点有默认命名空间,而你写了 "//item" 却没注册 XmlNamespaceManager。
- 先确认是否有命名空间:
doc.DocumentElement.NamespaceURI不为空就一定要处理 - 必须配
XmlNamespaceManager:var nsmgr = new XmlNamespaceManager(doc.NameTable);nsmgr.AddNamespace("ns", "http://example.com/ns");doc.SelectNodes("//ns:item", nsmgr); -
SelectSingleNode()返回null不代表语法错,只代表没匹配——建议先用doc.DocumentElement.OuterXml打印根结构,肉眼核对层级和标签名
修改后保存失败:Save() 覆盖原文件却内容变空?
这是典型的流未关闭或权限问题。调用 doc.Save("path.xml") 时,如果文件正被其他进程(如记事本、IDE)占用,.NET 不抛异常,而是静默写入 0 字节。
- 改用
FileStream显式控制:using (var fs = new FileStream("path.xml", FileMode.Create, FileAccess.Write, FileShare.None))doc.Save(fs); - 若需保留 BOM 或指定编码,别用字符串路径重载,改用
XmlWriter.Create(stream, new XmlWriterSettings { Encoding = Encoding.UTF8, Indent = true }) - 注意:
XmlDocument不自动格式化,Indent = true只在XmlWriter层生效,直接Save(string)不会缩进
性能差、内存暴涨:大 XML 别硬刚 XmlDocument
XmlDocument 是 DOM 模型,整个文档必须加载进内存。解析 50MB XML 时,实际内存占用常超 200MB,GC 压力大,且没有流式释放机制。
- 超过 1MB 就该考虑
XmlReader:它是只进、低内存的 SAX 风格,适合提取特定字段或流式转换 - 若必须用 DOM 但要减少开销,避免反复调用
GetElementsByTagName()——它每次遍历全树,缓存结果或改用 XPath +SelectNodes() - .NET Core 3.0+ 推荐转向
System.Xml.Linq.XDocument,API 更简洁,但底层仍是 DOM,大文件问题没本质改善
真正难的不是写对 XPath,而是判断该不该用 XmlDocument。一上来就 Load 整个配置包,等发现卡顿再切 XmlReader,重构成本远高于最初选型时多看两眼文件大小和访问模式。


















