如何在powershell中获取子XML节点的所有父节点
因此,我一直在处理一个XML文件,其中存储了一个文件夹结构,我希望能够验证文件系统中是否存在该文件夹结构。虽然有很多方法可以尝试执行此解析路径获取子项等。。。我希望能够构建XML结构中的路径,并将其放入字符串或其他内容中,我可以对其运行测试路径。听起来很简单,对吧?也许对于一些经验丰富的Powershell老兵来说可能是这样,但我对PS还不熟悉,所以很难理解这一点 下面是一个具有目录结构的XML示例如何在powershell中获取子XML节点的所有父节点,xml,powershell,Xml,Powershell,因此,我一直在处理一个XML文件,其中存储了一个文件夹结构,我希望能够验证文件系统中是否存在该文件夹结构。虽然有很多方法可以尝试执行此解析路径获取子项等。。。我希望能够构建XML结构中的路径,并将其放入字符串或其他内容中,我可以对其运行测试路径。听起来很简单,对吧?也许对于一些经验丰富的Powershell老兵来说可能是这样,但我对PS还不熟悉,所以很难理解这一点 下面是一个具有目录结构的XML示例 <config> <local> <set
<config>
<local>
<setup>
<folder name="FolderA" type="root">
<folder name="FolderC"/>
<folder name="FolderB">
<folder name="FolderD" type="thisone"/>
</folder>
</folder>
</setup>
</local>
</config>
对我来说很明显,为了得到完整的路径,你必须从最后开始,向后走节点树,我就在那里迷路了。
目的是从可能存在的多条路径中只选择一条路径。例如,我想检查FolderD的路径。因此,我必须只构建路径\FolderA\FolderB\FolderD,而忽略其余部分。最简单的方法可能是: 识别目标叶节点 在循环中向上遍历层次结构,并从name属性值构建路径,直到命中type=root节点 $xmlDoc=[xml]@' '@ 获取感兴趣的叶元素。 $leafNode=$xmlDoc.SelectSingleNode'//文件夹[@type=thisone]' 只需沿着节点层次结构建立路径 直到找到属性类型为root的元素。 $path=$leafNode.name $node=$leaftnode 而$node.type-ne'root'{ $node=$node.ParentNode $path=$node.name+'\'+$path'忽略此选项-修复语法高亮 } 输出结果路径: FolderA\FolderB\FolderD $path 原始答案,基于问题的原始形式:可能仍然对使用递归从元素属性自上而下构建路径感兴趣 使用递归,您仍然可以采用自上而下的方法:
$xmlDoc = [xml] @'
<folder name="FolderA">
<folder name="FolderC"/>
<folder name="FolderB">
<folder name="FolderD"/>
</folder>
</folder>
'@
# Define a function that walks the specified XML element's hierarchy to
# down its leaf elements, building up a path of the values of the
# specified attribute, and outputting the path for each leaf element.
function get-LeafAttribPaths($xmlEl, $attrName, $parentPath) {
$path = if ($parentPath) { join-path $parentPath $xmlEl.$attrName } else
{ $xmlEl.$attrName }
if ($xmlEl.ChildNodes.Count) { # interior element -> recurse over children
foreach ($childXmlEl in $xmlEl.ChildNodes) {
get-LeafAttribPaths $childXmlEl $attrName $path
}
} else { # leaf element -> output the built-up path
$path
}
}
# Invoke the function with the document element of the XML document
# at hand and the name of the attribute from whose values to build the path.
$paths = get-LeafAttribPaths $xmlDoc.DocumentElement 'name'
# Output the resulting paths.
# With the above sample input:
# FolderA\FolderC
# FolderA\FolderB\FolderD
$paths
# Test paths for existence.
Test-Path $paths
我会这样写:
$xmlDoc = [xml]'<folder name="FolderA">
<folder name="FolderC"/>
<folder name="FolderB">
<folder name="FolderD"/>
</folder>
</folder>'
function Get-XmlPath($node, $pathPrefix){
$children = $node.SelectNodes('folder[@name]')
$path = [System.IO.Path]::Combine($pathPrefix, $node.name)
if ($children.Count) {
foreach($child in $children) {
Get-XmlPath $child $path
}
} else {
$path
}
}
Get-XmlPath $xmlDoc.DocumentElement | % { [PSCustomObject]@{Path = $_; Exist = Test-Path $_ } }
更新以反映新的xml结构
可以将搜索范围缩小到选定节点,如下所示:
Get-XmlPath $xmlDoc.SelectSingleNode('//folder[@type="root"]') | % { [PSCustomObject]@{Path = $_; Exist = Test-Path $_ } }
关键是从正确的节点开始。如果不适合,您也可以尝试其他选择器:
#start from first folder node
$xmlDoc.SelectSingleNode('//folder')
#start from first folder node with root attribute
$xmlDoc.SelectSingleNode('//folder[@type="root"]')
给定一个起始子元素并从那里钻取,这对我来说很好:
function Get-XPath (
[object]$Element
) {
if ($Element.ParentNode -eq $Null) {
return "/"
}
else {
return (Get-XPath $Element.ParentNode) + "/" + $Element.Name
}
}
好的,看看你给出的例子,我写的XML看起来和我实际发布的略有不同。我的文件夹结构位于config/local/setup的XML路径中,我的文件夹元素也有一个type属性。我已经在帖子中更新了我的XML。更新的原因是,当我针对实际的XML运行代码时,我得到了\setup\FolderA\FolderC,而不是像您的示例那样的FolderA\FolderB。我为这个问题的误传道歉。@todd1215:很高兴听到这个消息;很好,重新循环-我已经相应地更新了答案。
#start from first folder node
$xmlDoc.SelectSingleNode('//folder')
#start from first folder node with root attribute
$xmlDoc.SelectSingleNode('//folder[@type="root"]')
function Get-XPath (
[object]$Element
) {
if ($Element.ParentNode -eq $Null) {
return "/"
}
else {
return (Get-XPath $Element.ParentNode) + "/" + $Element.Name
}
}