I'm struggling with recursive adding child nodes to XML in Powershell. The main goal is to update the XML files based on the provided file.
ex. XML looks like this:
<a>
<b>
<c1>Value0</c1>
</b>
</a>
and I want to add node with child nodes to make it looks like this (the order is not important):
<a>
<b>
<c>
<d1>Value1</d2>
<d2>Value2</d2>
<d3>Value3</d3>
</c>
<c1>Value0</c1>
</b>
</a>
I created two functions for this:
function InsertRecursively {
param (
$OriginalXml,
$DataToInsert
)
foreach ($key in $DataToInsert.Keys) {
if ($DataToInsert.$key.GetType().Name -eq "Hashtable") {
SetChildItem -XML $OriginalXml -ChildName $key
InsertRecursively -OriginalXml $OriginalXml.$key -DataToInsert $DataToInsert.$key
}
else {
SetChildItem -Xml $OriginalXml -ChildName $key -ChildValue $DataToInsert.$key
}
}
}
function SetChildItem {
param (
$Xml,
$ChildName,
$ChildValue
)
if ($null -eq $Xml.$ChildName) {
$child = $ssCont.CreateElement($ChildName)
if ($ChildValue) {
$child.InnerText = $ChildValue.ToString()
}
$Xml.AppendChild($child) > $null
Write-Host "Adding item '$ChildName'"
}
elseif (($null -ne $ChildValue) -and ($Xml.$ChildName -ne $ChildValue)) {
$Xml.$ChildName = $ChildValue.ToString()
Write-Host "Updating value of item '$ChildName'"
}
else {
Write-Host "Item '$ChildName' is set correctly"
}
return $Xml
}
When the function adds the first child to the existing node everything works fine, problem is when the function wants to add another child to this child node - then the $Xml parameter in the SetChildItem function is just an empty string, not a node where the function can add a child. The result is an error that the string does not contain a method AppendChild.
CodePudding user response:
Something simpler like the below:
$xml = [xml]@"
<a>
<b>
<c1>Value0</c1>
</b>
</a>
"@
$newElements = [xml]@"
<c>
<d1>Value1</d1>
<d2>Value2</d2>
<d3>Value3</d3>
</c>
"@
$newNode = $xml.ImportNode($newElements.get_DocumentElement(), $true)
$xml.SelectSingleNode('//b').AppendChild($newNode)
Should output
<a>
<b>
<c1>Value0</c1>
<c>
<d1>Value1</d1>
<d2>Value2</d2>
<d3>Value3</d3>
</c>
</b>
</a>
CodePudding user response:
Here is how I would do it with Xml Linq in c#. You can used the Xml Linq library with Power Shell.
string input = @"
<a>
<b>
<c1>Value0</c1>
</b>
</a>";
XDocument doc = XDocument.Parse(input);
XElement nodeB = doc.Descendants("b").FirstOrDefault();
XElement nodeC = new XElement("c", new object[] {
new XElement("d1", "Value1"),
new XElement("d2", "Value2"),
new XElement("d2", "Value3")
});
nodeB.Add(nodeC);
If you want the new node as first item use : nodeB.AddFirst(nodeC);