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
}