您好親愛的 Powershell 用戶,
我正在嘗試決議 xml 檔案,這些檔案的結構可能不同。因此,我想根據從變數接收到的節點結構訪問節點值。
例子
#XML file
$xml = [xml] @'
<node1>
<node2>
<node3>
<node4>test1</node4>
</node3>
</node2>
</node1>
'@
直接訪問這些值是有效的。
#access XML node directly -works-
$xml.node1.node2.node3.node4 # working <OK>
通過變數中的節點資訊訪問值不起作用。
#access XML node via path from variable -does not work-
$testnodepath = 'node1.node2.node3.node4'
$xml.$testnodepath # NOT working
$xml.$($testnodepath) # NOT working
有沒有辦法通過從變數接收節點資訊直接訪問 XML 節點值?
PS:我知道,有一種通過 Selectnode 的方法,但我認為這是低效的,因為它基本上是在搜索關鍵字。
#Working - but inefficient
$testnodepath = 'node1/node2/node3/node4'
$xml.SelectNodes($testnodepath)
我需要一種非常有效的方式來決議 XML 檔案,因為我需要決議巨大的 XML 檔案。有沒有辦法通過從變數接收節點結構來直接訪問 $xml.node1.node2.node3.node4 形式的節點值?
uj5u.com熱心網友回復:
你可以使用ExecutionContext ExpandString這個:
$ExecutionContext.InvokeCommand.ExpandString("`$(`$xml.$testnodepath)")
test1
如果節點路徑 ( $testnodepath) 來自外部(例如引數),您可能希望通過洗掉任何不是單詞字符或點 ( ) 的字符來防止任何惡意代碼注入.:
$securenodepath = $testnodepath -Replace '[^\w\.]'
$ExecutionContext.InvokeCommand.ExpandString("`$(`$xml.$securenodepath)")
uj5u.com熱心網友回復:
您可以將包含屬性路徑的字串拆分為單獨的名稱,然后逐個取消參考它們:
# define path
$testnodepath = 'node1.node2.node3.node4'
# create a new variable, this will be our intermediary for keeping track of each node/level we've resolved so far
$target = $xml
# now we just loop through each node name in the path
foreach($nodeName in $testnodepath.Split('.')){
# keep advancing down through the path, 1 node name at a time
$target = $target.$nodeName
}
# this now resolves to the same value as `$xml.node1.node2.node3.node4`
$target
uj5u.com熱心網友回復:
我需要決議巨大的 XML 檔案
下面介紹一種記憶體友好的流方法,它不需要將整個 XML 檔案 (DOM) 加載到記憶體中。因此,即使它們不適合記憶體,您也可以決議非常大的 XML 檔案。它還應該提高決議速度,因為我們可以簡單地跳過我們不感興趣的元素。為此,我們使用System.Xml.XmlReader動態處理 XML 元素,同時從檔案中讀取它們。
我已經將代碼包裝在一個可重用的函式中:
Function Import-XmlElementText( [String] $FilePath, [String[]] $ElementPath ) {
$stream = $reader = $null
try {
$stream = [IO.File]::OpenRead(( Convert-Path -LiteralPath $FilePath ))
$reader = [System.Xml.XmlReader]::Create( $stream )
$curElemPath = '' # The current location in the XML document
# While XML nodes are read from the file
while( $reader.Read() ) {
switch( $reader.NodeType ) {
([System.Xml.XmlNodeType]::Element) {
if( -not $reader.IsEmptyElement ) {
# Start of a non-empty element -> add to current path
$curElemPath = '/' $reader.Name
}
}
([System.Xml.XmlNodeType]::Text) {
# Element text -> collect if path matches
if( $curElemPath -in $ElementPath ) {
[PSCustomObject]@{
Path = $curElemPath
Value = $reader.Value
}
}
}
([System.Xml.XmlNodeType]::EndElement) {
# End of element - remove current element from the path
$curElemPath = $curElemPath.Substring( 0, $curElemPath.LastIndexOf('/') )
}
}
}
}
finally {
if( $reader ) { $reader.Close() }
if( $stream ) { $stream.Close() }
}
}
像這樣稱呼它:
Import-XmlElementText -FilePath test.xml -ElementPath '/node1/node2a/node3a', '/node1/node2b'
鑒于此輸入 XML:
<node1>
<node2a>
<node3a>test1</node3a>
<node3b/>
<node3c a='b'/>
<node3d></node3d>
</node2a>
<node2b>test2</node2b>
</node1>
產生此輸出:
Path Value
---- -----
/node1/node2a/node3a test1
/node1/node2b test2
實際上,該函式輸出的物件可以像往常一樣由管道命令處理或存盤在陣列中:
$foundElems = Import-XmlElementText -FilePath test.xml -ElementPath '/node1/node2a/node3a', '/node1/node2b'
$foundElems[1].Value # Prints 'test2'
筆記:
Convert-Pathis used to convert a PowerShell path (aka PSPath), which might be relative, to an absolute path that can be used by .NET functions. This is required because .NET uses a different current directory than PowerShell and a PowerShell path can be in a form that .NET doesn't even understand (e. g.Microsoft.PowerShell.Core\FileSystem::C:\something.txt).- When encountering start of an element, we have to skip empty elements such as
<node/>, because for such elements we don't enter theEndElementcase branch, which would render the current path ($curElemPath) invalid (the element would not be removed from the current path again).
轉載請註明出處,本文鏈接:https://www.uj5u.com/caozuo/434190.html
上一篇:遞回屬性計算?
