0

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.

2 Answers 2

1

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>
Sign up to request clarification or add additional context in comments.

1 Comment

Thanks this is very helpful, I forgot to mention that this function has to edit node if one exists and has a different value than requested. I have managed that using LINQ.
0

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);

1 Comment

Thanks, that helped me resolve my problem.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.