I am trying to add and remove elements from multiple xml files. i ran into two issue. it removed empty elements automatically and second it did not remove what i wanted to
I have my xml like this
<Entity>
<App>
<item1>1</item1>
<emptyItem/>
<person>
<itemToRemove>true</itemToRemove>
<emptyItem/>
<otheritem>1</otheritem>
</person>
<person>
<itemToRemove>false</itemToRemove>
<emptyItem/>
<otheritem>3</otheritem>
</person>
<person>
<itemToRemove>false</itemToRemove>
<emptyItem/>
<otheritem>3</otheritem>
</person>
</App>
</Entity>
what i want is
<Entity>
<App>
<item1>1</item1>
<emptyItem/>
<person>
<emptyItem/>
<otheritem>1</otheritem>
</person>
<person>
<emptyItem/>
<otheritem>3</otheritem>
</person>
<person>
<emptyItem/>
<otheritem>3</otheritem>
</person>
<newItemtoAdd>2001-01-01</newItemtoAdd>
</App>
</Entity>
i added newItemtoAdd element and removed itemToRemove on output xml above.
with the current script i have used what i get is this
<Entity>
<App>
<item1>1</item1>
<emptyItem/>
<person>
<itemToRemove>true</itemToRemove>
<otheritem>1</otheritem>
</person>
<person>
<itemToRemove>false</itemToRemove>
<otheritem>3</otheritem>
</person>
<person>
<itemToRemove>false</itemToRemove>
<otheritem>3</otheritem>
</person>
<newItemtoAdd>2001-01-01</newItemtoAdd>
</App>
</Entity>
emptyItem node is removed which i do not want to happen, newItemtoAdd is added which is good, and itemToRemove node is not removed which is not what i want
here is my script
Get-ChildItem D:\Projects\*.xml |
% {
[xml]$xml = [xml](Get-Content $_.fullname)
$newItemtoAdd = $xml.CreateElement('newItemtoAdd')
$newItemtoAdd.PsBase.InnerText = '1900-01-01'
$null = $xml.Entity.App.AppendChild($newItemtoAdd)
$xml.SelectNodes("//Entity/App/person") | ? {
$_.name -eq "itemToRemove" } | % {$_.ParentNode.RemoveChildNode($_) }
$xml.Save($_.FullName)
}
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
}
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.
PowerShell has two nice syntax features which might be used in this case:
Xml
dot notationWhere 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.
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>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With