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

时间:2016-07-29 00:29:00

标签: xml powershell

所以我一直在研究一个存储有文件夹结构的XML文件,我希望能够验证文件系统中是否存在文件夹结构。虽然有很多种方法可以尝试这样做,但是......#Resolve-Path" "获取-ChildItems"等...我希望能够构建XML结构中的路径并将其放入字符串或其他内容中,我可以针对它运行Test-Path。听起来很容易吧?很可能对于一些经验丰富的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

对我而言,似乎很明显,为了获得完整的路径,你必须从最后开始并向后走节点树,然后我迷路了。 目的是仅从可能存在的多个路径中选择一个路径。所以我想检查FolderD的路径。因此,我必须构建该路径\ FolderA \ FolderB \ FolderD并忽略其余路径。

3 个答案:

答案 0 :(得分:3)

最简单的方法可能是:

  • 识别目标叶节点
  • 在循环中向上遍历层次结构并从name属性值构建路径,直到type=root节点被点击
$xmlDoc = [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>
'@

# Get the leaf element of interest.
$leafNode = $xmlDoc.SelectSingleNode('//folder[@type="thisone"]')

# Simply build up the path by walking up the node hierarchy
# until an element with attribute type="root" is found.
$path = $leafNode.name
$node = $leafNode
while ($node.type -ne 'root') {
  $node = $node.ParentNode
  $path = $node.name + '\' + $path         #'# (ignore this - fixes syntax highglighting)
} 

# Output the resulting path:
#  FolderA\FolderB\FolderD
$path

原始回答,基于问题的原始形式:对于从元素属性自上而下构建路径,使用递归,可能仍然感兴趣。< / p>

通过递归,您仍然可以采用自上而下的方法:

$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

答案 1 :(得分:2)

我会这样写:

drop shadow

在我的系统中它产生:

$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结构

您可以按如下方式将搜索范围缩小到所选节点:

Path                        Exist
----                        -----
FolderA\FolderC             False
FolderA\FolderB\FolderD     False

关键是从正确的节点开始。如果不合适,您也可以尝试其他选择器:

Get-XmlPath $xmlDoc.SelectSingleNode('//folder[@type="root"]') | % { [PSCustomObject]@{Path = $_; Exist = Test-Path $_ } }

答案 2 :(得分:1)

给出一个起始(子)元素并从那里开始钻进,这对我来说很好:

function Get-XPath (
    [object]$Element
) {
    if ($Element.ParentNode -eq $Null) {
        return "/"
    }
    else {
        return (Get-XPath $Element.ParentNode) + "/" + $Element.Name
    }
}