如何在powershell中获取子XML节点的所有父节点

如何在powershell中获取子XML节点的所有父节点,xml,powershell,Xml,Powershell,因此,我一直在处理一个XML文件,其中存储了一个文件夹结构,我希望能够验证文件系统中是否存在该文件夹结构。虽然有很多方法可以尝试执行此解析路径获取子项等。。。我希望能够构建XML结构中的路径,并将其放入字符串或其他内容中,我可以对其运行测试路径。听起来很简单,对吧?也许对于一些经验丰富的Powershell老兵来说可能是这样,但我对PS还不熟悉,所以很难理解这一点 下面是一个具有目录结构的XML示例 <config> <local> <set

因此,我一直在处理一个XML文件,其中存储了一个文件夹结构,我希望能够验证文件系统中是否存在该文件夹结构。虽然有很多方法可以尝试执行此解析路径获取子项等。。。我希望能够构建XML结构中的路径,并将其放入字符串或其他内容中,我可以对其运行测试路径。听起来很简单,对吧?也许对于一些经验丰富的Powershell老兵来说可能是这样,但我对PS还不熟悉,所以很难理解这一点

下面是一个具有目录结构的XML示例

<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
    }
}