p0ck3t ace
p0ck3t ace

Reputation: 3

Foreach loop to add element to XML

I need to add a new element to existing nodes in an XML. The current structure essentially looks like this:

<CommandManagerResults>
   <ListReports>
    <Row>
      <Name>aaa</Name>
    </Row>
    <Row>
      <Name>bbb</Name>
    </Row>
   </ListReports>
   <ListDocuments>
    <Row>
      <Name>ccc</Name>
    </Row>
    <Row>
      <Name>ddd</Name>
    </Row>
   </ListDocuments>
</CommandManagerResults>

I need to add an element in all the "Row" nodes. The code I have so far:

$directory = "E:\temp"
cd $directory

[xml]$XmlDocument = Get-Content ".\test.xml"

$ProjectName = $XmlDocument.CreateElement("ProjectName")
$ProjectName.InnerText = "test"

$temp = $XmlDocument.SelectNodes("//Row")

foreach ($row in $temp){
    $row.AppendChild($ProjectName)
    $XmlDocument.Save($directory + '\test.xml')
}

However, only the very last "Row" node is getting saved with the new "ProjectName" element. I added '$row | FL' inside the foreach loop and it shows each Row as having the ProjectName element as being there through each iteration of the loop, unfortunately no matter if I save inside or after the foreach loop only the last Row node is saving with the ProjectName element. How it's coming out:

<CommandManagerResults>
   <ListReports>
    <Row>
      <Name>aaa</Name>
    </Row>
    <Row>
      <Name>bbb</Name>
    </Row>
   </ListReports>
   <ListDocuments>
    <Row>
      <Name>ccc</Name>
    </Row>
    <Row>
      <Name>ddd</Name>
      <ProjectName>Test</ProjectName>
    </Row>
   </ListDocuments>
</CommandManagerResults>

What I want the structure to look like in the end:

<CommandManagerResults>
   <ListReports>
    <Row>
      <Name>aaa</Name>
      <ProjectName>Test</ProjectName>
    </Row>
    <Row>
      <Name>bbb</Name>
      <ProjectName>Test</ProjectName>
    </Row>
   </ListReports>
   <ListDocuments>
    <Row>
      <Name>ccc</Name>
      <ProjectName>Test</ProjectName>
    </Row>
    <Row>
      <Name>ddd</Name>
      <ProjectName>Test</ProjectName>
    </Row>
   </ListDocuments>
</CommandManagerResults>

FYI, very new to Powershell and XMLs so hopefully everything I've said makes sense and I'm at least heading down the right path.

Upvotes: 0

Views: 501

Answers (1)

zett42
zett42

Reputation: 27776

You have to create a new element for each element to be added. Finally, the Save() call should be only at the end, when you are done with the document.

$temp = $XmlDocument.SelectNodes("//Row")

foreach ($row in $temp){

    $ProjectName = $XmlDocument.CreateElement("ProjectName")
    $ProjectName.InnerText = "test"

    $row.AppendChild($ProjectName)
}

$XmlDocument.Save($directory + '\test.xml')

On a side note [xml]$XmlDocument = Get-Content ".\test.xml", while being convenient, is bad practice. It works just by chance, as most XML documents these days are UTF-8 encoded, which happens to be the default encoding used by Get-Content. But Get-Content knows nothing about the actual value of the XML "encoding" attribute.

Proper way to load the XML document, while respecting its "encoding" attribute:

$xml = [xml]::new()
$xml.Load((Convert-Path ".\test.xml")) 

Upvotes: 1

Related Questions