So I've been working on an XML file that has a folder structure stored in it and I want to be able to validate that the folder structure exists in the file system. While there are many ways to try and do this "Resolve-Path" "Get-ChildItems" etc ... I want to be able to build the path that's in the XML structure and put it into a string or something to I can just run Test-Path against it. Sounds easy enough right? Well probably for some seasoned Powershell veterans it might be but I'm new to PS and having some difficulty wrapping my head around this.
Here's a sample of XML with the Directory Structure
<config>
<local>
<setup>
<folder name="FolderA" type="root">
<folder name="FolderC"/>
<folder name="FolderB">
<folder name="FolderD" type="thisone"/>
</folder>
</folder>
</setup>
</local>
</config>
If the child node was set to FolderD the ultimate goal is to build a string that resembles something like this
FolderA\FolderB\FolderD
It seems obvious to me that in order to get the full path you'd have to start at the end and walk the node tree backwards, and right there I get lost. The intention is to select only one path out of the multiple ones that potentially could exist. So for instance I want to check the path for FolderD. So I'd have to build just that path \FolderA\FolderB\FolderD and ignore the rest.
The simplest approach is probably:
name
attribute values until the type=root
node is hit$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
Original answer, based on the original form of the question: May still be of interest with respect to building paths from element attributes top-down, using recursion.
With recursion, you can still employ a top-down approach:
$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
I would write it that way:
$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 $_ } }
In my system it yields:
Path Exist
---- -----
FolderA\FolderC False
FolderA\FolderB\FolderD False
Updated to reflect new xml structure
You can narrow search to selected node as follows:
Get-XmlPath $xmlDoc.SelectSingleNode('//folder[@type="root"]') | % { [PSCustomObject]@{Path = $_; Exist = Test-Path $_ } }
The key is to start from correct node. You can try other selectors as well if this don't suit:
#start from first folder node
$xmlDoc.SelectSingleNode('//folder')
#start from first folder node with root attribute
$xmlDoc.SelectSingleNode('//folder[@type="root"]')
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With