HOWTO · PowerShell
如何使用 PowerShell 解析 XML 文件
這篇文章將討論如何使用 Windows PowerShell 正確解析 XML 檔案。
本頁內容
在 PowerShell 中擷取 XML 值時,可用 Select-Xml 搭配 XPath 運算式快速查詢;若需要瀏覽已載入的文件,可用 System.Xml.XmlDocument;若要以串流方式向前讀取,則可用 System.Xml.XmlTextReader。以下範例只讀取 XML,並以一份簡單的電腦清單說明元素文字與屬性值的差異。
使用 Select-Xml 解析 XML 元素與屬性
Select-Xml 會評估 XPath 運算式並傳回符合項目的物件。每個符合項目都會透過 Node 屬性提供所選取的 XML 節點。使用 InnerText 讀取元素文字;InnerXml 則會傳回元素內含的標記,適合需要將該內容當作 XML 使用時。
執行以下範例:
$path = Join-Path ([System.IO.Path]::GetTempPath()) "computers-$([guid]::NewGuid()).xml"
@'
<Computers>
<Computer name="WINPC-01" ip="127.0.0.1"><Name>WINPC-01</Name></Computer>
<Computer name="WINPC-02" ip="192.168.1.105"><Name>WINPC-02</Name></Computer>
</Computers>
'@ | Set-Content -Path $path -Encoding utf8
Write-Output 'Element names:'
Select-Xml -Path $path -XPath '/Computers/Computer/Name' |
ForEach-Object { $_.Node.InnerText }
Write-Output 'Attribute names:'
Select-Xml -Path $path -XPath '/Computers/Computer' |
ForEach-Object { $_.Node.GetAttribute('name') }
Remove-Item -Path $path
Element names:
WINPC-01
WINPC-02
Attribute names:
WINPC-01
WINPC-02
XPath /Computers/Computer/Name 會從根節點沿著階層找到每個 Name 元素。若要選取屬性,請選取 Computer 元素,再從每個符合項目的節點讀取 name 屬性。範例輸出分成兩組:Element names 下方列出元素文字,Attribute names 下方列出 name 屬性的值;這兩個標籤維持英文。請依照 XML 的結構調整 XPath,元素名稱及大小寫都必須與文件相符。如果 XML 內容已存在字串中,Select-Xml 也可透過 -Content 參數接收。
使用 System.Xml.XmlDocument 載入並查詢 XML
System.Xml.XmlDocument 會將文件載入為可瀏覽的樹狀結構。若指令碼需要執行多個相關查詢,或需要在父節點與子節點之間移動,這種方式就很方便。缺點是整份文件都會保留在記憶體中,因此處理非常大的檔案時,串流讀取器可能更合適。
此範例會載入暫存 XML 檔案、選取每部電腦,並在讀取前確認是否有 Name 子元素。如果 XML 結構描述允許缺少該元素,這項檢查就很重要;找不到符合項目時,SelectSingleNode() 會傳回 $null。
$path = Join-Path ([System.IO.Path]::GetTempPath()) "computers-$([guid]::NewGuid()).xml"
@'
<Computers>
<Computer><Name>WINPC-01</Name></Computer>
<Computer><Ip>192.168.1.105</Ip></Computer>
</Computers>
'@ | Set-Content -Path $path -Encoding utf8
$document = [System.Xml.XmlDocument]::new()
$document.Load($path)
foreach ($computer in $document.SelectNodes('/Computers/Computer')) {
$nameNode = $computer.SelectSingleNode('Name')
if ($null -ne $nameNode) {
Write-Output $nameNode.InnerText
}
else {
Write-Output 'No Name element'
}
}
Remove-Item -Path $path
讀取文字值時請使用 InnerText;InnerXml 會傳回元素內嵌的任何標記。如果文件使用預設 XML 命名空間,未加前綴的 XPath 運算式可能無法匹配其中的元素;請建立 XmlNamespaceManager,並在查詢中使用前綴。
使用 System.Xml.XmlTextReader 以串流方式讀取 XML
System.Xml.XmlTextReader 會依文件順序提供 XML 節點,並以唯讀、只能向前移動的方式讀取。它不會建立完整的文件樹,因此處理大型輸入時可減少記憶體使用量。由於讀取器無法向後瀏覽,請在相關節點讀取到時擷取所需值。請在 finally 區塊中關閉讀取器,即使處理提早中止,也能釋放檔案控制代碼。
以下範例會從每個 Computer 起始元素讀取 name 屬性。範例會自行建立 XML 檔案,並在處理完成後釋放讀取器。
$path = Join-Path ([System.IO.Path]::GetTempPath()) "computers-$([guid]::NewGuid()).xml"
@'
<Computers>
<Computer name="WINPC-01" ip="127.0.0.1" />
<Computer name="WINPC-02" ip="192.168.1.105" />
</Computers>
'@ | Set-Content -Path $path -Encoding utf8
$reader = [System.Xml.XmlTextReader]::new($path)
try {
while ($reader.Read()) {
if ($reader.NodeType -eq [System.Xml.XmlNodeType]::Element -and
$reader.Name -eq 'Computer') {
Write-Output $reader.GetAttribute('name')
}
}
}
finally {
$reader.Dispose()
Remove-Item -Path $path
}
若要讀取元素文字而非屬性,請檢查元素名稱是否相符,並在讀取器位於該元素時讀取文字。ReadString() 會讀取文字內容並推進讀取器,因此在較複雜的迴圈中,請留意讀取器位置的變化。完成後請釋放讀取器;若讓它保持開啟,輸入檔案可能會持續被鎖定。
選擇適合的 PowerShell XML 解析方式
如果使用簡短的 XPath 查詢,就能清楚地從檔案或字串中擷取少數節點,請選擇 Select-Xml。若需要方便地瀏覽樹狀結構、執行多個查詢,或存取相關節點,請選擇 System.Xml.XmlDocument。若可以依序處理節點,且希望避免將完整樹狀結構載入記憶體,請選擇 System.Xml.XmlTextReader。
這三種方式都能讀取元素與屬性。主要差異在於它們呈現文件的方式:Select-Xml 傳回 XPath 的符合項目,XmlDocument 可在已載入的樹狀結構中瀏覽,而 XmlTextReader 則會以串流方式向前讀取。請依文件大小與查詢方式選擇合適的方法;如果輸入可能缺少節點或屬性,也要先檢查它們是否存在。