XML 處理指令以 <?target data?> 形式存在,供特定應用程式解讀,不屬於文件資料樹一部分。常見範例:
<?xml version="1.0" encoding="utf-8"?> (XML 宣告,特殊 PI)<?xml-stylesheet href="style.xsl" type="text/xsl"?> (樣式表關聯)<?mso-application progid="InfoPath.Document"?> (Office InfoPath 指示)| 屬性/方法 | 說明 |
|---|---|
Target | 目標名稱 (如 xml-stylesheet、mso-application) |
Value / Data | 資料字串 (如 href="style.xsl" type="text/xsl") |
OwnerDocument | 所屬 XmlDocument |
using System;
using System.Xml;
public class XmlPiManager
{
private readonly XmlDocument _doc;
private readonly string _filePath;
public XmlPiManager(string filePath)
{
_filePath = filePath;
_doc = new XmlDocument { XmlResolver = null }; // 安全:禁用外部資源解析
_doc.Load(filePath);
}
///
/// 讀取指定 Target 的 PI 值
///
public string ReadPi(string target)
{
// XPath: processing-instruction('target')
var node = _doc.SelectSingleNode($"/processing-instruction('{target}')");
return (node as XmlProcessingInstruction)?.Value;
}
///
/// 修改或建立 PI
///
public void SetPi(string target, string value)
{
var pi = _doc.SelectSingleNode($"/processing-instruction('{target}')")
as XmlProcessingInstruction;
if (pi != null)
{
pi.Value = value; // 直接修改 Value 即可
}
else
{
// 建立新 PI 並插入文件開頭 (XML 宣告之後)
pi = _doc.CreateProcessingInstruction(target, value);
// 找到第一個非 PI 節點之前插入
XmlNode insertBefore = null;
foreach (XmlNode n in _doc.ChildNodes)
{
if (n.NodeType != XmlNodeType.ProcessingInstruction &&
n.NodeType != XmlNodeType.XmlDeclaration)
{
insertBefore = n;
break;
}
}
if (insertBefore != null)
_doc.InsertBefore(pi, insertBefore);
else
_doc.AppendChild(pi);
}
_doc.Save(_filePath);
}
///
/// 刪除指定 Target 的 PI
///
public void RemovePi(string target)
{
var node = _doc.SelectSingleNode($"/processing-instruction('{target}')");
if (node != null)
{
_doc.RemoveChild(node);
_doc.Save(_filePath);
}
}
///
/// 列印所有 PI
public void PrintAllPi()
{
foreach (XmlNode node in _doc.ChildNodes)
{
if (node.NodeType == XmlNodeType.ProcessingInstruction)
{
var pi = (XmlProcessingInstruction)node;
Console.WriteLine($"Target: {pi.Target}, Value: {pi.Value}");
}
}
}
}
// 使用範例
class Program
{
static void Main()
{
var mgr = new XmlPiManager("config.xml");
mgr.PrintAllPi();
// Target: xml-stylesheet, Value: href="style.xsl" type="text/xsl"
// Target: mso-application, Value: progid="InfoPath.Document"
string style = mgr.ReadPi("xml-stylesheet");
Console.WriteLine($"樣式表:{style}");
mgr.SetPi("custom-pi", "data=123;mode=test");
mgr.RemovePi("mso-application");
}
}
| 查詢需求 | XPath 運算式 |
|---|---|
| 所有 PI | /processing-instruction() |
| 特定 Target | /processing-instruction('target-name') |
| 包含特定字串的 PI | /processing-instruction()[contains(., 'href')] |
// XML 宣告非標準 PI,屬性為 XmlDeclaration
var decl = _doc.FirstChild as XmlDeclaration;
if (decl != null)
{
Console.WriteLine($"Version: {decl.Version}, Encoding: {decl.Encoding}, Standalone: {decl.Standalone}");
// 修改編碼
decl.Encoding = "utf-8";
_doc.Save(_filePath);
}
PI 不屬於任何命名空間,Target 名稱慣例使用 prefix-name 避免衝突 (如 mso-application、xml-stylesheet)。
XmlResolver = null:防止 XXE (XML External Entity) 攻擊,禁用 DTD/外部實體解析。XmlReader 串流讀取 PI,避免完整載入 DOM。Save 會重寫整個檔案,大檔案考慮備份機制。
public static string ReadPiStreaming(string filePath, string target)
{
using (var reader = XmlReader.Create(filePath, new XmlReaderSettings
{
XmlResolver = null,
IgnoreProcessingInstructions = false // 關鍵:預設忽略 PI,必須設 false
}))
{
while (reader.Read())
{
if (reader.NodeType == XmlNodeType.ProcessingInstruction &&
reader.Name == target)
{
return reader.Value;
}
}
}
return null;
}