How can I group and sum the input XML so duplicate Item elements are merged?
<Inventory>
<Item>
<ItemType>P</ItemType>
<ItemID>37494</ItemID>
<Color>11</Color>
<MinQty>1</MinQty>
</Item>
<Item>
<ItemType>P</ItemType>
<ItemID>50254</ItemID>
<Color>11</Color>
<MinQty>4</MinQty>
</Item>
<Item>
<ItemType>P</ItemType>
<ItemID>37494</ItemID>
<Color>11</Color>
<MinQty>2</MinQty>
</Item>
</Inventory>
<Inventory>
<Item>
<ItemType>P</ItemType>
<ItemID>37494</ItemID>
<Color>11</Color>
<MinQty>3</MinQty>
</Item>
<Item>
<ItemType>P</ItemType>
<ItemID>50254</ItemID>
<Color>11</Color>
<MinQty>4</MinQty>
</Item>
</Inventory>
Current idea is based on this answer, using Group-Object. However I don't see how to combine this with the summing of a sub-element. Remove Duplicate XML Node Groups in Powershell
A Group-Object
solution that modifies the XML document in place:
# Parse the XML file into a DOM.
# Replace "sample.xml" with your XML file path.
($xmlDoc = [xml]::new()).Load((Convert-Path sample.xml))
# Loop over all <Item> elements.
$xmlDoc.Inventory.Item |
Group-Object ItemType, ItemId, Color |
Where-Object Count -gt 1 |
ForEach-Object {
# Sum up all MinQty values and assign the sum to the first element
# in the group.
$_.Group[0].MinQty = [string] ($_.Group.MinQty | Measure-Object -Sum).Sum
# Now remove all other elements in the group.
$null = $_.Group[1..($_.Count-1)].ForEach({ $_.ParentNode.RemoveChild($_) })
}
Note:
Casting the .Sum
value to [string]
explicitly is only required in Windows PowerShell, where only an actual string ([string]
instance) is accepted on assignment to an XML element.
PowerShell (Core) 7+, more conveniently performs stringification on demand.
A simple example:
# OK in PS 7+: integer 2 is automatically converted to string.
# In WinPS, you get the following error:
# Cannot set "el" because only strings can be used as
# values to set XmlNode properties.'
($x = [xml] '<el>1</el>').el = 2
# Required in Windows PowerShell: RHS must be an actual string.
($x = [xml] '<el>1</el>').el = [string] 2 # simple alternative here: '2'
Printing the XML text of the updated DOM, as shown below, matches the expected result in your question:
if ($IsCoreCLR) { # PowerShell (Core) 7+
# Pretty-print
([System.Xml.Linq.XDocument] $xmlDoc.OuterXml).ToString()
} else { # Windows PowerShell
# No pretty-printing easily available, just print .OuterXml
$xmlDoc.OuterXml
}
Collected from the Internet
Please contact [email protected] to delete if infringement.
Comments