目录
- 引言
- 方法原理
- 使用示例
- 优点总结
- 扩展思路
- 完整代码
引言
在处理大型 XML 文档时,传统的 XDocument.Load 会一次性将整个文档加载到内存中,对于大文件非常消耗资源。尤其在只需要部分节点的场景下,这种方式既浪费内存,又影响性能。
本文将分享一个按路径流式遍历 XML 节点的实用方法,支持深层嵌套路径,并保持流式解析,内存占用低,非常适合大文件处理。
方法原理
流式解析
使用 XmlReader 逐节点读取 XML 文档,而不是一次性加载整个文档,节省内存。
按路径过滤节点
用户只需提供路径,例如 "set/fields/field",方法会按照路径递归查找目标元素。
XNode.ReadFrom 创建完整 XElement
当读到目标元素时,使用 XNode.ReadFrom(reader) 构建 XElement,包括子节点及结束标签,并将 XmlReader 定位到下一个兄弟节点,保证流式遍历不中断。
递归获取子节点
对于多级路径,编程使用递归方法从 XElement 访问子节点
使用示例
假设有如下 XML:
<set>
<fields>
<field>
<fieldname>rq</fieldname>
<datatype>字符</datatype>
</field>
<field>
<fieldname>djbh</fieldname>
<datatype>字符</datatype>
</field>
</fields>
</set>
调用:
string xmlstr = File.ReadAllText("example.xml");
var fields = XmlStreamHelper.StreamElements(xmlstr, "set/fields/field");
Console.WriteLine("字段数量:" + fields.Count());
foreach (var f in fields)
{
Console.WriteLine(f.Element("fieldname")?.Value);
}
输出:
字段数量:2 rq djbh
优点总结
- 内存占用低:大 XML 文件也能按需遍历,不会一次性加载整个文档。
- 路径灵活:支持任意深度路径,按需获取节点。
- 避免 ReadSubtree 陷阱:使用
XNode.ReadFrom与递归子节点,状态管理清晰。 - 延迟执行:返回
IEnumerable<XElement>,可以直接在foreach中流式消费。
扩展思路
- 可以在路径中增加通配符
*或//,支持 XPath-like 查找。 - 可以结合
yield return,实现大文件边解析边javascript处理,适合数据清洗、日志分析、报表导出等场景。
完整代码
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Xml;
using System.Xml.Linq;
public static class XmlStreamHelper
{
/// <summary>
/// 按路径流式遍历 XjsML 节点,路径用 / 分隔
/// </summary>
/// <param name="xmlstr">XML 字符串</param>
/// <param name="xpath">节点路径,例如 "set/fields/fhttp://www.devze.comield"</param>
public static IEnumerable<XElement> StreamElements(string xmlstr, string xpath)
{
if (string.IsNullOrWhiteSpace(xmlstr) || string.IsNullOrWhiteSpace(xpath))
yield break;
var nodes = xpath.Split('/');
using var reader = XmlReader.Create(new StringReader(xmlstr), new XmlReaderSettings { IgnoreWhitespace = true, IgnoreComments = true });
reader.MoveToContent();
if (nodes.Length == 1)
{
if (XNode.ReadFrom(reader) is XElement el)
{
foreach (var e in el.Elements(xpath))
{
编程 yield return e;
}
}
}
else
{
while (reader.Read())
{
if (reader.NodeType != XmlNodeType.Element) continue;
if (reader.Name == nodes[0] && XNode.ReadFrom(reader) is XElement el)
{
foreach (var child in GetElementsRecursive(el, nodes, 1))
yield return child;
}
}
}
}
/// <summary>
/// 递归获取子节点
/// </summary>
private static IEnumerable<XElement> GetElementsRecursive(XElement el, string[] nodes, int index)
{
if (index == nodes.Length - 1)
return el.Elements(nodes[index]);
var sub = el.Element(nodes[index]);
return sub == null ? Enumerable.Empty<XElement>() : GetElementsRecursive(sub, nodes, index + 1);
}
}
到此这篇关于C#按路径流式遍历XML节点的实用方法的文章就介绍到这了,更多相关C#流式遍历XML节点内容请搜索编程客栈(www.devze.com)以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程客栈(www.devze.com)!
加载中,请稍侯......
精彩评论