Public/ConvertFrom-ConnectorXmlToPowerShellObject.ps1
|
<# .SYNOPSIS Converts an XML node to a JSON-like object (i.e. a PowerShell object with properties corresponding to the XML structure). .DESCRIPTION Recursively converts an XML node and its children to a PowerShell object that can be easily .EXAMPLE [xml] $xml = Get-Content -Path "data.xml" $object = $xml | ConvertFrom-ConnectorXmlToPowerShellObject #> function ConvertFrom-ConnectorXmlToPowerShellObject { [CmdletBinding()] param ( [Parameter(Mandatory = $true, ValueFromPipeline = $true)] [ValidateNotNull()] [System.Xml.XmlNode]$Node, # If a node has no attributes and a single child with a node type of 'Text', then return the innerText value of that child node rather than a separate object # This eliminates '#text' child nodes where possible [Parameter(Mandatory = $false)] [switch]$IncludeChildTextNodes ) Process { $NODE_TYPES = @( [System.Xml.XmlNodeType]::Element, [System.Xml.XmlNodeType]::Attribute, [System.Xml.XmlNodeType]::Text ) if ($Node.NodeType -eq [System.Xml.XmlNodeType]::Text) { Write-Debug ("Returning child node '{0}' as text because NodeType is '{1}'" -f $Node.Name, $Node.NodeType) return $Node.InnerText } if (!$IncludeChildTextNodes.IsPresent -and $Node.HasChildNodes -and $Node.ChildNodes.Count -eq 1 -and !$Node.HasAttributes) { $child = $Node.ChildNodes | Select-Object -First 1 if ($child.NodeType -eq [System.Xml.XmlNodeType]::Text) { Write-Debug ("Returning child '{0}' node '{1}' as text for parent '{2}' because the parent has no attributes and the child text node is the only node. Pass IncludeChildTextNodes to prevent this behaviour." -f $child.NodeType, $childName, $Node.Name) return $child.InnerText } } $nodeHt = ([System.Management.Automation.OrderedHashtable]@{}) foreach ($attribute in $Node.Attributes) { if ($nodeHt.Contains($attribute.Name)) { Write-Warning ("Skipping attribute '{0}' on node '{1}' because the name is a duplicate" -f $attribute.Name, $Node.Name) continue } $thisObject = ConvertFrom-XmlToJSON $attribute -IncludeChildTextNodes:$IncludeChildTextNodes.IsPresent if ($null -ne $thisObject) { Write-Debug ("Recursively adding attribute '{0}' for parent node '{1}'" -f $attribute.Name, $Node.Name) $nodeHt += @{ $attribute.Name = $thisObject } } else { Write-Warning ("Omitting attribute '{0}' for parent '{1}' because the result was null." -f $attribute.Name, $Node.Name) } } # Skip stuff we're not interested in, e.g. declarations foreach ($child in $Node.ChildNodes.Where({ $_.NodeType -in $NODE_TYPES })) { $childName = $child.ToString() if ($nodeHt.Contains($childName)) { # Duplicate node name -- assume an array if ($null -ne $nodeHt[$childName] -and $nodeHt[$childName].GetType().Name.StartsWith('List')) { Write-Debug ("Duplicate child node name '{0}' of type '{1}' on parent node '{2}' -- using existing collection" -f $childName, $child.NodeType, $Node.Name) $nodes = $nodeHt[$childName] } else { Write-Verbose ("Duplicate child node name '{0}' of type '{1}' on parent node '{2}' -- assuming a collection of nodes" -f $childName, $child.NodeType, $Node.Name) $nodes = New-Object System.Collections.Generic.List[object] if ($null -ne $nodeHt[$childName]) { $nodes.Add(($nodeHt[$childName])) } } $thisObject = ConvertFrom-XmlToJSON $child -IncludeChildTextNodes:$IncludeChildTextNodes.IsPresent if ($null -ne $thisObject) { $nodes.Add(($thisObject)) } else { Write-Warning ("Omitting child '{0}' node '{1}' for parent '{2}' (index {3}) because the result was null." -f $child.NodeType, $childName, $Node.Name, $nodes.Count) } $nodeHt[$childName] = $nodes } else { $thisObject = ConvertFrom-XmlToJSON $child -IncludeChildTextNodes:$IncludeChildTextNodes.IsPresent if ($null -ne $thisObject) { Write-Debug ("Recursively adding child node '{0}' of type '{1}' for parent node '{2}'" -f $childName, $child.NodeType, $Node.Name) $nodeHt += @{ $childName = $thisObject } } else { Write-Warning ("Omitting child '{0}' node '{1}' for parent '{2}' because the result was null." -f $child.NodeType, $childName, $Node.Name) } } } return (New-Object PSObject -Property $nodeHt) } } |