Adding and Removing Xmlnode using Powershell

PowerShell has two nice syntax features which might be used in this case:

1. Xml dot notation

Where you might directly reference a node in your xml file.
For example, to select the itemToRemove for the first (0 based) Person:

$Xml.Entity.App.person[0].itemToRemove
true

Unfortunately this PowerShell feature is a little overdone as you can see from the example where instead of returning an XmlElement, it returns a string with the innerText (#Text) if it concerns a leaf note, see: #16878 Decorate dot selected Xml strings (leaves) with XmlElement methods.

2. Member-Access Enumeration

You can use member enumeration to get property values from all members of a collection. When you use the member access operator (.) with a member name on a collection object, such as an array, if the collection object does not have a member of that name, the items of the collection are enumerated and PowerShell looks for that member on each item. This applies to both property and method members.

Taking the xml file in the question as an example:

$Xml.Entity.App.person.itemToRemove
true
false
false

Note that in this example, there is no index behind the Person property, meaning that all persons are enumerated and returned.
To overcome a possible returned string (described in 1. xml dot notation) you might enumerate the SelectNodes() on each person node instead and use that to removed the concerned nodes from their parent:

$Xml.Entity.App.person.SelectNodes('itemToRemove').ForEach{ $Null = $_.ParentNode.RemoveChild($_) }
[System.Xml.Linq.XDocument]::Parse($Xml.OuterXml).ToString()

<Entity>
  <App>
    <item1>1</item1>
    <emptyItem />
    <person>
      <emptyItem />
      <otheritem>1</otheritem>
    </person>
    <person>
      <emptyItem />
      <otheritem>3</otheritem>
    </person>
    <person>
      <emptyItem />
      <otheritem>3</otheritem>
    </person>
  </App>
</Entity>

To remove a node from all xml-files in a directory, and save result as a new xml-file, I use this powershell-script:

Get-ChildItem .\*.xml | % {
    [Xml]$xml = Get-Content $_.FullName
    $xml | Select-Xml -XPath '//*[local-name() = ''itemToRemove'']' | ForEach-Object{$_.Node.ParentNode.RemoveChild($_.Node)}
    $xml.OuterXml | Out-File .\result.xml -encoding "UTF8"
} 

Note: the "local-name"-syntax is for ignoring namespaces.


Get-ChildItem D:\Projects\*.xml | % {
    [Xml]$xml = Get-Content $_.FullName

    $newItemtoAdd = $xml.CreateElement('newItemtoAdd')
    $newItemtoAdd.PsBase.InnerText = '1900-01-01'
    $xml.Entity.App.AppendChild($newItemtoAdd) | Out-Null

    $parent_xpath = '/Entity/App/person'
    $nodes = $xml.SelectNodes($parent_xpath)
    $nodes | % {
        $child_node = $_.SelectSingleNode('itemToRemove')
        $_.RemoveChild($child_node) | Out-Null
    }

    $xml.OuterXml | Out-File $_.FullName
}