XML 是跨平台資料交換的標準格式。在 .NET 中讀取 XML 主要有兩種模型:
XmlReader 實作,提供更高彈性與效能。本文深入解析 XmlTextReader 的核心用法,並封裝可重複使用的 XmlReaderHelper 類別。
| 成員 | 說明 |
|---|---|
Read() | 移動到下一個節點,讀完傳回 false |
NodeType | 目前節點型別(XmlNodeType.Element、Text、Attribute 等) |
Name / LocalName | 節點名稱(含/不含前置詞) |
Value | 節點文字內容 |
HasAttributes | 是否擁有屬性 |
AttributeCount | 屬性數量 |
MoveToNextAttribute() | 移動到下一個屬性節點 |
NamespaceURI、Prefix | 命名空間 URI 與前置詞 |
using System;
using System.Xml;
using System.Windows.Forms;
using System.Collections.Generic;
public class XmlReaderHelper : IDisposable
{
private readonly string _xmlPath;
private readonly ListBox _listBox;
private XmlTextReader _reader;
private const string ErrPrefix = "ErrorOccurredWhileReading";
public XmlReaderHelper(string xmlPath, ListBox listBox)
{
_xmlPath = xmlPath;
_listBox = listBox;
}
// 釋放資源
public void Dispose()
{
_reader?.Close();
_reader = null;
GC.SuppressFinalize(this);
}
// 1. 完整遍歷所有節點值
public void ReadAllValues()
{
ExecuteReader(reader => _listBox.Items.Add(reader.Value));
}
// 2. 顯示節點型別
public void ReadNodeTypes()
{
ExecuteReader(reader => _listBox.Items.Add(reader.NodeType.ToString()));
}
// 3. 依節點型別過濾
public void FilterByNodeType(params XmlNodeType[] types)
{
ExecuteReader(reader => {
foreach (var t in types)
if (reader.NodeType == t)
_listBox.Items.Add($"{reader.Name} is Type {reader.NodeType}");
});
}
// 4. 僅讀取文字節點
public void ReadTextNodes()
{
ExecuteReader(reader => {
if (reader.NodeType == XmlNodeType.Text)
_listBox.Items.Add(reader.Value);
});
}
// 5. 讀取所有元素屬性
public void ReadAttributes()
{
ExecuteReader(reader => {
if (reader.NodeType == XmlNodeType.Element)
{
if (reader.HasAttributes)
{
_listBox.Items.Add($"Element {reader.Name} has {reader.AttributeCount} attributes:");
while (reader.MoveToNextAttribute())
_listBox.Items.Add($"{reader.Name} = {reader.Value}");
reader.MoveToElement(); // 回到元素節點
}
else
_listBox.Items.Add($"Element {reader.Name} has no attribute");
_listBox.Items.Add("");
}
});
}
// 6. 讀取命名空間
public void ReadNamespaces()
{
ExecuteReader(reader => {
if (reader.NodeType == XmlNodeType.Element)
{
ShowNamespace(reader);
if (reader.HasAttributes)
{
while (reader.MoveToNextAttribute())
ShowNamespace(reader);
reader.MoveToElement();
}
}
});
}
private void ShowNamespace(XmlTextReader r)
{
if (!string.IsNullOrEmpty(r.Prefix))
_listBox.Items.Add($"Prefix '{r.Prefix}' => Namespace '{r.NamespaceURI}' (LocalName: {r.LocalName})");
}
// 7. 完整重建 XML 結構顯示
public void ReadFullStructure()
{
ExecuteReader(reader => {
string line = "";
switch (reader.NodeType)
{
case XmlNodeType.XmlDeclaration:
line = $"{reader.Name} {reader.Value}?>";
break;
case XmlNodeType.Element:
line = $"<{reader.Name}";
if (reader.HasAttributes)
{
while (reader.MoveToNextAttribute())
line += $" {reader.Name}='{reader.Value}'";
reader.MoveToElement();
}
line += ">";
break;
case XmlNodeType.EndElement:
line = $"{reader.Name}>";
break;
case XmlNodeType.Text:
line = reader.Value;
break;
}
if (!string.IsNullOrEmpty(line))
_listBox.Items.Add(line);
});
}
// 共用執行邏輯
private void ExecuteReader(Action action)
{
_listBox.Items.Clear();
_reader = new XmlTextReader(_xmlPath);
try
{
while (_reader.Read())
action(_reader);
}
catch (XmlException ex)
{
throw new XmlException($"{ErrPrefix} {_xmlPath}: {ex.Message}");
}
finally
{
_reader?.Close();
}
}
}
public partial class MainForm : Form
{
private const string SampleXml = "sample.xml";
private XmlReaderHelper _helper;
public MainForm()
{
InitializeComponent();
}
private void btnReadAll_Click(object sender, EventArgs e)
{
RunHelper(h => h.ReadAllValues());
}
private void btnNodeTypes_Click(object sender, EventArgs e)
{
RunHelper(h => h.ReadNodeTypes());
}
private void btnFilter_Click(object sender, EventArgs e)
{
RunHelper(h => h.FilterByNodeType(
XmlNodeType.Element, XmlNodeType.EndElement, XmlNodeType.XmlDeclaration));
}
private void btnTextOnly_Click(object sender, EventArgs e)
{
RunHelper(h => h.ReadTextNodes());
}
private void btnAttributes_Click(object sender, EventArgs e)
{
RunHelper(h => h.ReadAttributes());
}
private void btnNamespaces_Click(object sender, EventArgs e)
{
RunHelper(h => h.ReadNamespaces());
}
private void btnFullXml_Click(object sender, EventArgs e)
{
RunHelper(h => h.ReadFullStructure());
}
private void RunHelper(Action action)
{
_helper = new XmlReaderHelper(SampleXml, lstOutput);
try { action(_helper); }
catch (XmlException ex) { MessageBox.Show(ex.Message, "XML Error", MessageBoxButtons.OK, MessageBoxIcon.Error); }
catch (Exception ex) { MessageBox.Show(ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); }
finally { _helper.Dispose(); }
}
}
<?xml version="1.0" encoding="utf-8"?>
<INVOICES DATE="28 11 2001" xmlns:cat="uri:Business-Categories">
<CUSTOMERS CUSTNAME="CHILEWINESINC" PHONE="124192356332" EMAIL="CW@YAHOO.COM.CN" CUSTID="192398" DELIVERY="INTERNATIONAL" OFFERID="27">
<cat:BUSINFO>WineDivisionSouth</cat:BUSINFO>
<cat:BUSINFO>BeerDivisionNorth</cat:BUSINFO>
<ORDER ORDERID="OID921" BATCHID="123">
<DETAILS>
<ITEMS NUM="2">
<ITEM>RancaguaWhile</ITEM>
<ITEM>RancaguaRed</ITEM>
</ITEMS>
</DETAILS>
</ORDER>
<ORDER ORDERID="OID927">
<DETAILS>
<ITEMS NUM="5">
<ITEM>ChillanRed</ITEM>
<ITEM>RancaguaWhile</ITEM>
<ITEM>SantiagoRed</ITEM>
<ITEM>RancaguaWhile</ITEM>
<ITEM>RancaguaRed</ITEM>
</ITEMS>
</DETAILS>
</ORDER>
</CUSTOMERS>
</INVOICES>
XmlDocument 或 XDocument (LINQ to XML)。XmlTextReader 是 .NET 處理大型 XML 的利器。透過封裝通用 Helper 類別,可大幅減少重複程式碼,專注於業務邏輯。實務上搭配 XmlReaderSettings 可啟用 DTD/Schema 驗證、忽略空白節點等進階功能,進一步提升健壯度。