Search code examples
xmlpowershell-5.0

Unable to add a new element in xml using powershell


I'm trying to add a new element in my xml file using powershell.

XML File (can't change the current content except adding a new element under Languages):

<UserSettings>
  <ApplicationIdentity version="16.0" />
  <Category name="License Header Manager_Languages" Category="{d1b5984c-1693-4f26-891e-0ba3bf5760b4}" Package="{4c570677-8476-4d33-bd0c-da36c89287c8}" RegisteredName="License Header Manager_Languages" PackageName="LicenseHeadersPackage">
    <PropertyValue name="LanguagesSerialized">&lt;Languages&gt;
  &lt;Language LineComment="//" BeginComment="/*" EndComment="*/" BeginRegion="#region" EndRegion="#endregion" SkipExpression=""&gt;
    &lt;Extensions&gt;
      &lt;Extension&gt;.cs&lt;/Extension&gt;
    &lt;/Extensions&gt;
  &lt;/Language&gt;
  &lt;Language LineComment="'" BeginComment="" EndComment="" BeginRegion="#Region" EndRegion="#End Region" SkipExpression=""&gt;
    &lt;Extensions&gt;
      &lt;Extension&gt;.vb&lt;/Extension&gt;
    &lt;/Extensions&gt;
  &lt;/Language&gt;
  &lt;/Languages&gt;
  </PropertyValue>
 </Category>
</UserSettings>

I'm trying to add add a new language with different extensions using following code

$file = "C:\Users\TempUser\Downloads\CurrentSettings.xml"

[xml]$xml = Get-Content $file

$extensions = @(
    ,@( @("#", "<#", "#>", ""), @(".ps1", ".psm1"))
    ,@( @("//", "/*", "*/", ""), @(".js", ".ts")))
$AttributeNames = @("LineComment", "BeginComment", "EndComment", "SkipExpression")

$xml.UserSettings.Category.PropertyValue | Where-Object { $_.name -eq "LanguagesSerialized" } | %{ 

    foreach ($elements in $extensions) {
        $lang = $xml.CreateElement("Language")   

        for ($j=0; $j -lt $AttributeNames.length; $j++) {
            $lang.SetAttribute($AttributeNames[$j], $elements[0][$j]) 
        }

        $extsElement = $lang.AppendChild($xml.CreateElement("Extensions"))
            
        foreach($ext in $elements[1]){
            $extElement = $extsElement.AppendChild($xml.CreateElement("Extension"))
            $extElement.AppendChild($xml.CreateTextNode($ext))
        }

        $_.AppendChild($lang)
    }
      
}

$xml.Save($file)

Expected result:

  &lt;Language LineComment="#" BeginComment="@*" EndComment="*@" BeginRegion="" EndRegion="" SkipExpression=""&gt;
    &lt;Extensions&gt;
      &lt;Extension&gt;.ps1&lt;/Extension&gt;
      &lt;Extension&gt;.psm1&lt;/Extension&gt;
    &lt;/Extensions&gt;
  &lt;/Language&gt;

Current Output in file (It adds the elements after Languages Tag):

&lt;/Language&gt;
&lt;/Languages&gt;<Language LineComment="#" BeginComment="<#" EndComment="#>" SkipExpression=""><Extensions><Extension>.ps1</Extension><Extension>.psm1</Extension></Extensions></Language><Language LineComment="//" BeginComment="/*" EndComment="*/" SkipExpression=""><Extensions><Extension>.js</Extension><Extension>.ts</Extension></Extensions></Language></PropertyValue>
    <PropertyValue name="Version">3.0.3</PropertyValue>
  </Category>
</UserSettings>

Appreciate any help, thanks!


Solution

  • Thanks, guys! In my case, I can't encode and save as < and > in the file as that is another parser's job. I have to add the language before it goes to this parser. Previously, I was adding the child element outside the main Languages node. Here's the updated solution which adds the child language node into the main Languages node.

    $file = "C:\Users\TempUser\Downloads\CurrentSettings.xml"
    [xml]$xml = Get-Content $file
    $extensions = @(
        ,@( @("#", "<#", "#>", ""), @(".ps1", ".psm1"))
        ,@( @("//", "/*", "*/", ""), @(".js", ".ts")))
    $AttributeNames = @("LineComment", "BeginComment", "EndComment", "SkipExpression")
    
    $xml.UserSettings.Category.PropertyValue | Where-Object { $_.name -eq "LanguagesSerialized" } | %{
        [xml]$Settings = $_.'#text' 
    
        foreach ($elements in $ExtensionDetails) {
            $lang = $Settings.CreateElement("Language")   
            for ($j=0; $j -lt $AttributeNames.length; $j++) {
                $lang.SetAttribute($AttributeNames[$j], $elements[0][$j]) 
            }
    
            $extsElement = $lang.AppendChild($Settings.CreateElement("Extensions")) 
            foreach($ext in $elements[1]){
                $extElement = $extsElement.AppendChild($Settings.CreateElement("Extension"))
                $extElement.AppendChild($Settings.CreateTextNode($ext))
            }
            $Settings.Languages.AppendChild($lang)
        } 
        $_.'#text' = $Settings.InnerXml
    }
    
    $xml.Save($file)
    

    the output is

        ...
        ...
        &lt;/Language&gt;
        &lt;Language LineComment="#" BeginComment="@*" EndComment="*@" SkipExpression=""&gt;
        &lt;Extensions&gt;
            &lt;Extension&gt;.ps1&lt;/Extension&gt;
            &lt;Extension&gt;.psm1&lt;/Extension&gt;
        &lt;/Extensions&gt;
        &lt;/Language&gt;
        &lt;Language LineComment="//" BeginComment="/*" EndComment="*/" SkipExpression=""&gt;
        &lt;Extensions&gt;
            &lt;Extension&gt;.js&lt;/Extension&gt;
            &lt;Extension&gt;.ts&lt;/Extension&gt;
        &lt;/Extensions&gt;
        &lt;/Language&gt;
    &lt;/Languages&gt;