Search code examples
xmlpowershellpowershell-7.0

Trouble writing nested XML elements in PowerShell


I have an app that has a config file in XML format. I have created a powershell script that allows users to select from multiple configuration changes without manually editing the config. It works great for single elements. For example, if you choose "Config 1" below it works exactly as expected. It finds any matching elements, like fruit and replaces that line. Great. However, if a config element has child elements, it strips out the XML and puts it all as one value. For example, if you select "Config 2" below it would write this:

<fruit>banana</fruit>
<drink>tea</drink>
<dishes>forkbowl</dishes>

How can I fix this?

Here is Configurations.xml

<EnvironmentData>
  <demo friendlyName="Config 1">
    <fruit>apple</fruit>
    <drink>Soda</drink>
  </demo>
  <demo friendlyName="Config 2">
    <fruit>banana</fruit>
    <drink>tea</drink>
      <dishes>
        <utensil>fork</utensil>
        <dish>bowl</dish>
      </dishes>
  </demo>
</EnvironmentData>

Here is the code:

#load data into hash
$envVariableHash = @{}
$test = $envs.SelectNodes("//$envName")[0]
ForEach ($ele in $test.ChildNodes) {
    #Write-Host "$($ele.LocalName): $($ele.InnerText)"
    $envVariableHash.add($ele.LocalName, $ele.InnerText)
}

#read build config and replace matches
$buildConfigFile = [System.Xml.XmlDocument](Get-Content "$($config.localbuildspath)/$($buildName)/config.xml");
$buildConfig = $buildConfigFile.ConfigData;

ForEach ($ele in $buildConfig.ChildNodes) {
    if ($envVariableHash.ContainsKey($ele.LocalName)) {
        $ele.InnerText = $envVariableHash[$ele.LocalName]
        if ($isDev) { Write-Host "Updated $($ele.LocalName)" -ForegroundColor DarkGray }
    }
}
Write-Host "Saving File... " -NoNewline -ForegroundColor DarkGray
$buildConfigFile.Save("$($config.localbuildspath)/$($buildName)/config.xml")
Write-Host "Done" -ForegroundColor DarkGray

Solution

  • You are using .InnerText to get the content of a node. But .InnerText only returns the values of its nodes. Use .InnerXml instead:

    1. Change line 6 from $envVariableHash.add($ele.LocalName, $ele.InnerText) to $envVariableHash.add($ele.LocalName, $ele.InnerXml).
    2. Change line 15 from $ele.InnerText = ... to $ele.InnerXml = ....