XML 基礎概念速覽
| 節點類型 | 範例 | 說明 |
| Element | <book>...</book> | 標籤配對構成的元素 |
| Attribute | id="10001" | 元素屬性 |
| Text | <name>書名</name> 中的「書名」 | 元素內文字內容 |
| Comment | <!-- 備註 --> | 註解節點 |
範例 XML 結構
<?xml version="1.0" encoding="UTF-8"?>
<library>
<books id="計算機類">
<book id="10001">
<name>書名1</name>
<publish>出版社1</publish>
<price>價格1</price>
</book>
<book id="10001">...</book>
</books>
<books id="人文類">...</books>
</library>
核心操作:XmlDocument + XPath
var xdoc = new XmlDocument();
xdoc.Load(Server.MapPath("~/App_Data/books.xml"));
// 1. 精確定位單一節點 (最常用)
string xpath = "//books[@id='計算機類']//book[@id='10001']";
XmlNode node = xdoc.DocumentElement.SelectSingleNode(xpath);
// 2. 取得子節點集合
XmlNodeList children = node.ChildNodes;
foreach (XmlNode child in children)
{
Console.WriteLine($"{child.Name}: {child.InnerText}");
}
// 3. 取得屬性值
string bookId = node.Attributes["id"]?.Value;
// 4. 多條件 / 聯集查詢
// 聯集:兩個路徑合併
xpath = "//books[@id='計算機類']//book[@id='10001'] | //books[@id='人文類']//book[@id='20001']";
// 通配符:所有子孫 name 節點
xpath = "//books[@id='計算機類']//*/name";
// 文字節點直接取值
xpath = "//books[@id='計算機類']//book[@id='10001']//name/text()";
string name = xdoc.DocumentElement.SelectSingleNode(xpath)?.Value;
XPath 語法速查表
| 語法 | 含義 | 範例 |
// | 任意深度子孫 | //book 所有 book 節點 |
/ | 直接子代 | /library/books |
[@attr='val'] | 屬性過濾 | //book[@id='10001'] |
text() | 文字節點 | //name/text() |
* | 任意元素 | //books/* 所有 books 直系子元素 |
| | 聯集 | //a | //b |
[position()=1] | 第一個 | //book[1] |
[last()] | 最後一個 | //book[last()] |
contains() | 包含文字 | //book[contains(name, 'C#')] |
XML 編輯操作:增刪改
// 建立輔助方法簡化操作
public static class XmlHelper
{
public static XmlAttribute CreateAttr(this XmlDocument doc, string name, string value)
{
var attr = doc.CreateAttribute(name);
attr.Value = value;
return attr;
}
public static XmlElement CreateElem(this XmlDocument doc, string name, string innerText = null)
{
var elem = doc.CreateElement(name);
if (innerText != null) elem.InnerText = innerText;
return elem;
}
}
// 1. 新增屬性
XmlNode book = xdoc.SelectSingleNode("//book[@id='10001']");
book.Attributes.Append(xdoc.CreateAttr("isbn", "978-7-121-12345-6"));
// 2. 刪除屬性
book.Attributes.RemoveNamedNode("isbn");
// 3. 新增子元素
XmlElement newBook = xdoc.CreateElem("book");
newBook.Attributes.Append(xdoc.CreateAttr("id", "10003"));
newBook.AppendChild(xdoc.CreateElem("name", "新書名"));
newBook.AppendChild(xdoc.CreateElem("publish", "新出版社"));
newBook.AppendChild(xdoc.CreateElem("price", "99.9"));
xdoc.SelectSingleNode("//books[@id='計算機類']").AppendChild(newBook);
// 4. 刪除子元素
XmlNode oldBook = xdoc.SelectSingleNode("//book[@id='10001']");
oldBook.ParentNode.RemoveChild(oldBook);
// 5. 替換子元素
XmlNode newNode = xdoc.CreateElem("book");
newNode.Attributes.Append(xdoc.CreateAttr("id", "10001"));
// ...填充內容
oldBook.ParentNode.ReplaceChild(newNode, oldBook);
// 6. 修改文字內容
XmlNode priceNode = xdoc.SelectSingleNode("//book[@id='10001']/price");
priceNode.InnerText = "88.8";
// 7. 儲存變更
xdoc.Save(Server.MapPath("~/App_Data/books.xml"));
資料綁定:XML 繫結到 GridView/Repeater
// 方法一:XmlDataSource 控制項 (宣告式)
<asp:XmlDataSource ID="XmlDS" runat="server"
DataFile="~/App_Data/books.xml"
XPath="//books[@id='計算機類']//book" />
<asp:GridView ID="gvBooks" runat="server" DataSourceID="XmlDS" AutoGenerateColumns="False">
<Columns>
<asp:BoundField DataField="id" HeaderText="ID" ReadOnly="True" />
<asp:BoundField DataField="name" HeaderText="書名" />
<asp:BoundField DataField="price" HeaderText="價格" />
</Columns>
</asp:GridView>
// 方法二:程式碼後置 DataTable 轉換 (靈活性高)
public DataTable XmlToDataTable(string xmlPath, string xpath)
{
var dt = new DataTable();
dt.Columns.Add("Id"); dt.Columns.Add("Name"); dt.Columns.Add("Price");
var doc = new XmlDocument();
doc.Load(xmlPath);
var nodes = doc.SelectNodes(xpath);
foreach (XmlNode node in nodes)
{
var row = dt.NewRow();
row["Id"] = node.Attributes["id"]?.Value;
row["Name"] = node.SelectSingleNode("name")?.InnerText;
row["Price"] = node.SelectSingleNode("price")?.InnerText;
dt.Rows.Add(row);
}
return dt;
}
// 使用
gvBooks.DataSource = XmlToDataTable(Server.MapPath("~/books.xml"), "//book");
gvBooks.DataBind();
效能優化:大型 XML 處理策略
| 場景 | 問題 | 解決方案 |
| XML > 10MB | XmlDocument 全載入記憶體 | 使用 XmlReader 流式讀取 (前向唯讀、低記憶體) |
| 頻繁查詢 | 重複 Load/Parse | 快取 XmlDocument (MemoryCache + 檔案監視失效) |
| 複雜 XPath | SelectNodes 效能差 | 預先建立 索引字典 (ID → 節點引用) |
| 寫入頻繁 | Save 重寫整個檔案 | 改用 SQLite/資料庫,XML 僅作匯出格式 |
XmlReader 流式讀取範例
public IEnumerable StreamBooks(string xmlPath)
{
using (var reader = XmlReader.Create(xmlPath))
{
while (reader.Read())
{
if (reader.NodeType == XmlNodeType.Element && reader.Name == "book")
{
string id = reader.GetAttribute("id");
reader.ReadToDescendant("name");
string name = reader.ReadElementContentAsString();
reader.ReadToNextSibling("publish");
string publish = reader.ReadElementContentAsString();
reader.ReadToNextSibling("price");
string price = reader.ReadElementContentAsString();
yield return new Book { Id = id, Name = name, Publish = publish, Price = price };
}
}
}
}
現代化替代:LINQ to XML (System.Xml.Linq)
using System.Xml.Linq;
var xdoc = XDocument.Load(path);
// 查詢
var books = xdoc.Descendants("book")
.Where(b => b.Attribute("id")?.Value == "10001")
.Select(b => new {
Id = b.Attribute("id").Value,
Name = b.Element("name")?.Value,
Price = b.Element("price")?.Value
});
// 建立/修改
var newBook = new XElement("book",
new XAttribute("id", "10003"),
new XElement("name", "LINQ to XML 實戰"),
new XElement("price", "120")
);
xdoc.Root.Element("books").Add(newBook);
xdoc.Save(path);
總結
ASP.NET XML 操作核心在於 XmlDocument + XPath 的組合:SelectSingleNode/SelectNodes 精確定位、屬性/文字節點分離存取、編輯後 Save 持久化。大型/高頻場景必須轉向 XmlReader 流式處理、快取策略、或改用資料庫。新專案強烈建議採用 LINQ to XML (XDocument),語法更現代、型別安全、支援 LINQ 查詢、功能更完整 (命名空間、註解保留、序列化整合)。