首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> 關於C語言

C# XML 讀取全攻略:XmlTextReader 流式處理實戰

     編輯:關於C語言

前言:為什麼選擇流式讀取?

XML 是跨平台資料交換的標準格式。在 .NET 中讀取 XML 主要有兩種模型:

  • DOM (Document Object Model):一次性載入整份文件到記憶體,支援隨機存取、XPath 查詢、編輯修改。缺點是大型文件會佔用大量記憶體。
  • 流模型:逐節點讀取,記憶體佔用極低,適合大型 XML。分為「推模型」(SAX,事件驅動)與「拉模型」。.NET 採用拉模型,由 XmlReader 實作,提供更高彈性與效能。

本文深入解析 XmlTextReader 的核心用法,並封裝可重複使用的 XmlReaderHelper 類別。

XmlTextReader 核心屬性與方法

成員說明
Read()移動到下一個節點,讀完傳回 false
NodeType目前節點型別(XmlNodeType.Element、Text、Attribute 等)
Name / LocalName節點名稱(含/不含前置詞)
Value節點文字內容
HasAttributes是否擁有屬性
AttributeCount屬性數量
MoveToNextAttribute()移動到下一個屬性節點
NamespaceURI、Prefix命名空間 URI 與前置詞

封裝通用讀取器:XmlReaderHelper

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 = $"";
                    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 = $"";
                    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();
        }
    }
}

WinForms 使用範例

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(); }
    }
}

測試用 sample.xml 範例

<?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>

效能與記憶體優勢

  • 記憶體:僅保留目前節點,無論 XML 多大,記憶體占用恆定。
  • 速度:單向讀取,無建立 DOM 樹的開銷,適合一次性匯入/轉換作業。
  • 限制:唯讀、唯前向,無法隨機存取或修改。若需編輯,請改用 XmlDocument 或 XDocument (LINQ to XML)。

結語

XmlTextReader 是 .NET 處理大型 XML 的利器。透過封裝通用 Helper 類別,可大幅減少重複程式碼,專注於業務邏輯。實務上搭配 XmlReaderSettings 可啟用 DTD/Schema 驗證、忽略空白節點等進階功能,進一步提升健壯度。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved