XmlReader(抽象基類)、XmlTextReader(實作)提供**前向唯讀、流式**存取 XML,記憶體占用極低,適合大型檔案(>100MB)或高效能場景。對比 XmlDocument(DOM、全載入記憶體)與 XDocument(LINQ to XML、較重),XmlReader 是串流處理的首選。
| 成員 | 用途 |
|---|---|
Read() | 移至下一節點,回傳 bool;結束時回傳 false。 |
NodeType | 當前節點型別:Element、EndElement、Text、XmlDeclaration、Attribute 等。 |
Name / LocalName | 節點名稱(含/不含前綴)。 |
Value | 文字節點內容、屬性值等。 |
HasAttributes | 是否有屬性。 |
MoveToNextAttribute() | 在屬性間游標移動。 |
GetAttribute(name) | 直接取得指定屬性值。 |
ReadElementContentAsString() | 讀取元素文字內容並自動移至 EndElement。 |
ReadInnerXml() / ReadOuterXml() | 取得片段 XML 字串。 |
using System;
using System.Xml;
using System.Collections.Generic;
public sealed class XmlStreamReader : IDisposable {
private readonly XmlReader _reader;
private bool _disposed;
public XmlStreamReader(string filePath, XmlReaderSettings settings = null) {
_reader = XmlReader.Create(filePath, settings ?? DefaultSettings());
}
public XmlStreamReader(Stream stream, XmlReaderSettings settings = null) {
_reader = XmlReader.Create(stream, settings ?? DefaultSettings());
}
private static XmlReaderSettings DefaultSettings() => new() {
IgnoreWhitespace = true, // 忽略非意義空白
IgnoreComments = true, // 忽略註解
DtdProcessing = DtdProcessing.Ignore, // 或 Prohibit 防 XXE
MaxCharactersFromEntities = 1024, // 防實體擴展攻擊
Async = false // 同步模式;需非同步改 true 配合 ReadAsync
};
/// 逐節點迴圈,回呼處理邏輯
public void Process(Action onNode) {
while (_reader.Read()) onNode(_reader);
}
/// 尋找特定元素並讀取內容
public IEnumerable ReadElements(string elementName) {
while (_reader.Read()) {
if (_reader.NodeType == XmlNodeType.Element && _reader.Name == elementName) {
yield return _reader.ReadElementContentAsString();
}
}
}
/// 讀取元素及其屬性為字典
public IEnumerable> ReadElementsWithAttributes(string elementName) {
while (_reader.Read()) {
if (_reader.NodeType == XmlNodeType.Element && _reader.Name == elementName) {
var dict = new Dictionary();
if (_reader.HasAttributes) {
while (_reader.MoveToNextAttribute()) {
dict[_reader.Name] = _reader.Value;
}
_reader.MoveToElement(); // 重要:移回元素節點
}
dict["_text"] = _reader.ReadElementContentAsString();
yield return dict;
}
}
}
public void Dispose() {
if (!_disposed) {
_reader?.Close();
_disposed = true;
}
}
}
// XML 範例:
//
// C# in Depth
// Three Body
//
using (var reader = new XmlStreamReader("books.xml")) {
// 1. 簡單列印所有節點
reader.Process(r => Console.WriteLine($"{r.NodeType}: {r.Name} = {r.Value}"));
// 2. 只讀取書名
foreach (var title in reader.ReadElements("title")) {
Console.WriteLine($"Title: {title}");
}
// 3. 讀取 book 元素完整資訊
foreach (var book in reader.ReadElementsWithAttributes("book")) {
Console.WriteLine($"ID={book["id"]}, Category={book["category"]}, Title={book["_text"]}");
}
}
Async=true,使用 await reader.ReadAsync(),配合 ReadElementContentAsStringAsync(),避免阻塞 UI/執行緒池。settings.ValidationType = ValidationType.Schema; settings.Schemas.Add(...); settings.ValidationEventHandler += (s,e)=>...;reader.NamespaceURI、reader.Prefix,或 reader.GetAttribute("name", "nsUri")。ReadSubtree() 取得子樹獨立 Reader,局部處理後釋放。Read() 會異常。DtdProcessing.Ignore/Prohibit。Read()。StreamReader 指定編碼後傳給 XmlReader.Create。XmlReader 是 .NET XML 處理的基石,掌握其流式模型、游標語意與安全設定,能編寫高效能、低記憶體、抗攻擊的 XML 解析器。封裝為 XmlStreamReader 類別後,業務代碼更聚焦於資料提取邏輯,提升可讀性與可測試性。