Stuck on how to get all children/grandchildren/recursive and sort

I have a script that compares objects and writes them to 3 new XMLs based on if they are ==, =>, or <=.
The issue I ma trying to solve is that the team that is sending the XML is not sorting repeatable fields so I get false positives.

My goal is to have the children groups below to be sorted.
Meaning:
I want the child tags sorted both/multiple in order. The order doesn’t really matter as I will be sorting both compare objects so when my if statements compare the objects the order will be the same.


[xml]$ReferenceXML = @"
<Rt>
    <CUDATE>
        <RecId>SDFGHJKLJGHJKHL764830SLCOLU2002</RecId>
        <PtySpData>
            <DtTm>2023-09-04T18:35:38Z</DtTm>
            <RptNtty>
                <NUM>548787777BYUC1BFR06N38</NUM>
            </RptNtty>
            <CP>
                <RCP>
                    <NameNum>
                        <NUM>54656789300UE5IV80HGVDE63</NUM>
                    </NameNum>
                    <Ntr>
                        <F>
                            <Df>YUIO</Df>
                        </F>
                    </Ntr>
                </RCP>
                <OCP>
                    <NameNum>
                        <Name>
                            <NUM>549300BV8YXI1GVJKG40</NUM>
                        </Name>
                    </NameNum>
                </OCP>
                <NttyRpt>
                    <NUM>549300UE5IV677868780HGVDE63</NUM>
                </NttyRpt>
            </CP>
        </PtySpData>
        <Ln>
            <SctLn>
                <EDt>2023-09-04</EDt>
                <UnqIdr>QWERTYUIOPKHJKHL</UnqIdr>
                <MAg>
                    <Pt1>
                        <Pt2>DERP</Pt2>
                    </Pt1>
                    <Vrn>2356</Vrn>
                </MAg>
            </SctLn>
        </Ln>
        <Coll>
            <SctLnd>
                <Cosd>
                    <Asst>
                        <Sct>
                            <Name>GB123456789</Name>
                            <Type>DFGHJJK</Type>
                            <NmnlV>
                                <NV>
                                    <A Ddy="NPD">12</A>
                                </NV>
                            </NmnlV>
                            <UP>
                                <MVal>
                                    <A Ddy="NPD">500</A>
                                </MVal>
                            </UP>
                            <kktV>
                                <Amt Ddy="NPD">501</Amt>
                            </kktV>
                            <Ql>YUIU</Ql>
                            <Mtty>2010-10-04</Mtty>
                            <Dsr>
                                <NameNum>
                                    <NUM>549300CDO03FKIZ7UK53</NUM>
                                </NameNum>
                                <JursCty>TX</JursCty>
                            </Dsr>
                            <Pt>
                                <Cd>HOUS</Cd>
                            </Pt>
                            <HrMrgn>99</HrMrgn>
                            <AvblUuse>true</AvblUuse>
                        </Sct>
                        <Sct>
                            <Name>GB123456789</Name>
                            <Type>FFGHXX</Type>
                            <NmnlV>
                                <NV>
                                    <A Ddy="NPD">5</A>
                                </NV>
                            </NmnlV>
                            <UP>
                                <MntryVal>
                                    <A Ddy="NPD">100</A>
                                </MntryVal>
                            </UP>
                            <MktVal>
                                <A Ddy="NPD">101</A>
                            </MktVal>
                            <Ql>OIUY</Ql>
                            <Mtty>2023-10-04</Mtty>
                            <Dsr>
                                <NameNum>
                                    <NUM>549300CDO03FKIZ7UK53</NUM>
                                </NameNum>
                                <JursCty>KS</JursCty>
                            </Dsr>
                            <Pt>
                                <Cd>OKIY</Cd>
                            </Pt>
                            <HrMrgn>99</HrMrgn>
                            <AvblUuse>true</AvblUuse>
                        </Sct>
                    </Asst>
                    <NetInd>false</NetInd>
                </Cosd>
            </SctLnd>
        </Coll>
    </CUDATE>
</Rt>
"@
$test = $ReferenceXML.Rt.CUDATE.Coll.SctLnd.Cosd.Asst
       Foreach($Node in $test){
            
            If ($Node.ChildNodes.Count -gt 1){
               $Node | Select-Xml 'AsstTp' | Select-Object -expand Node | sort '#text'
               Write-Host "Node value: $($Node.OuterXml)"
            }
      }

To make it clearer I would like the XML file to look like this:

[xml]$ReferenceXML = @"
<Rt>
    <CUDATE>
        <RecId>SDFGHJKLJGHJKHL764830SLCOLU2002</RecId>
        <PtySpData>
            <DtTm>2023-09-04T18:35:38Z</DtTm>
            <RptNtty>
                <NUM>548787777BYUC1BFR06N38</NUM>
            </RptNtty>
            <CP>
                <RCP>
                    <NameNum>
                        <NUM>54656789300UE5IV80HGVDE63</NUM>
                    </NameNum>
                    <Ntr>
                        <F>
                            <Df>YUIO</Df>
                        </F>
                    </Ntr>
                </RCP>
                <OCP>
                    <NameNum>
                        <Name>
                            <NUM>549300BV8YXI1GVJKG40</NUM>
                        </Name>
                    </NameNum>
                </OCP>
                <NttyRpt>
                    <NUM>549300UE5IV677868780HGVDE63</NUM>
                </NttyRpt>
            </CP>
        </PtySpData>
        <Ln>
            <SctLn>
                <EDt>2023-09-04</EDt>
                <UnqIdr>QWERTYUIOPKHJKHL</UnqIdr>
                <MAg>
                    <Pt1>
                        <Pt2>DERP</Pt2>
                    </Pt1>
                    <Vrn>2356</Vrn>
                </MAg>
            </SctLn>
        </Ln>
        <Coll>
            <SctLnd>
                <Cosd>
                    <Asst>
                        
                        <Sct>
                            <Name>GB123456789</Name>
                            <Type>FFGHXX</Type>
                            <NmnlV>
                                <NV>
                                    <A Ddy="NPD">5</A>
                                </NV>
                            </NmnlV>
                            <UP>
                                <MntryVal>
                                    <A Ddy="NPD">100</A>
                                </MntryVal>
                            </UP>
                            <MktVal>
                                <A Ddy="NPD">101</A>
                            </MktVal>
                            <Ql>OIUY</Ql>
                            <Mtty>2023-10-04</Mtty>
                            <Dsr>
                                <NameNum>
                                    <NUM>549300CDO03FKIZ7UK53</NUM>
                                </NameNum>
                                <JursCty>KS</JursCty>
                            </Dsr>
                            <Pt>
                                <Cd>OKIY</Cd>
                            </Pt>
                            <HrMrgn>99</HrMrgn>
                            <AvblUuse>true</AvblUuse>
                        </Sct>
                        <Sct>
                            <Name>GB123456789</Name>
                            <Type>DFGHJJK</Type>
                            <NmnlV>
                                <NV>
                                    <A Ddy="NPD">12</A>
                                </NV>
                            </NmnlV>
                            <UP>
                                <MVal>
                                    <A Ddy="NPD">500</A>
                                </MVal>
                            </UP>
                            <kktV>
                                <Amt Ddy="NPD">501</Amt>
                            </kktV>
                            <Ql>YUIU</Ql>
                            <Mtty>2010-10-04</Mtty>
                            <Dsr>
                                <NameNum>
                                    <NUM>549300CDO03FKIZ7UK53</NUM>
                                </NameNum>
                                <JursCty>TX</JursCty>
                            </Dsr>
                            <Pt>
                                <Cd>HOUS</Cd>
                            </Pt>
                            <HrMrgn>99</HrMrgn>
                            <AvblUuse>true</AvblUuse>
                        </Sct>
                    </Asst>
                    <NetInd>false</NetInd>
                </Cosd>
            </SctLnd>
        </Coll>
    </CUDATE>
</Rt>
"@

If you look that the first XML you see that the first and second Sct have been switched. Now I am not sure what I would sort on because Sct are the same but there is a difference DFGHJJK.

I would like to capture both and sort them based on but I need that sort to be with the original and not a copy.

Here is the compare function that has been set up:

Function Compare-XML-Files {
    [CmdletBinding()]
    Param (
        $Path = $(Get-Location),
        $ReferenceFile,
        $ComparedFile
    )
    Begin {
        Set-Location $Path
        Write-Host "Comparing XML Values"
        $MatchedValues       = New-Object System.Collections.Generic.List[PSObject]
        $ActualReportedValues = New-Object System.Collections.Generic.List[PSObject]
        $ExpectsReportedValues  = New-Object System.Collections.Generic.List[PSObject]
        $ReferenceXML = [xml]''
        $ComparedXML  = [xml]'' 
        $MatchedFileNew  = $ReferenceFile -replace '.xml','_Matched_Both.xml' 
        $ActualFileNew   = $ReferenceFile -replace '.xml','_Actual_Only.xml'
        $ExpectsFileNew  = $ReferenceFile -replace '.xml','_Expects_Only.xml'               
    }
    Process {
        $ReferenceXML.Load($("$ExpectsFile"))
        $Header         = $ReferenceXML.BizData.OuterXml -replace "<Trad>.*", '<Trad>'
        $Footer         = $ReferenceXML.BizData.OuterXml -replace ".*</Trad>", '</Trad>'        
        $RefTradData    = $ReferenceXML.BizData.Pyld.Document.ChildNodes.TradData
        $RefTrades      = $RefTradData.ChildNodes.OuterXml -replace "\sxmlns*.+?>", '>'
        
        $ComparedXML.Load($("$ActualFile"))
        $ComparedHeader = $ComparedXML.BizData.OuterXml -replace "<Trad>.*", '<Trad>'
        $ComparedFooter = $ComparedXML.BizData.OuterXml -replace ".*</Trad>", '</Trad>'
        $ComTradData    = $ComparedXML.BizData.Pyld.Document.ChildNodes.TradData
        $ComTrades      = $ComTradData.ChildNodes.OuterXml -replace "\sxmlns*.+?>", '>'        
        
        $test = $ReferenceXML.Rt.CUDATE.Coll.SctLnd.Cosd.Asst
       Foreach($Node in $test){
            
            If ($Node.ChildNodes.Count -gt 1){
                
               $Node | Select-Xml 'AsstTp' | Select-Object -expand Node | sort '#text'
                Write-Host "Node value: $($Node.OuterXml)"
 
                }
              
            }
            
       }
      

        $XMLObjects = @{
            ReferenceObject  = $RefTrades
            DifferenceObject = $ComTrades
        }
        $ComparedXML = Compare-Object @XMLObjects -IncludeEqual
        ForEach ($Result in $ComparedXML) {
            If ($Result.SideIndicator -eq '==') {
                $ComparedValue = [ordered] @{
                    Trade = [string]$Result.InputObject
                }
                $null = $MatchedValues.Add($(New-Object PSObject -Property $ComparedValue))
            } 
            ElseIf ($Result.SideIndicator -eq '=>') {
                $ComparedValue  = [ordered] @{
                    Trade = [string]$Result.InputObject
                }
                $null = $ActualReportedValues.Add($(New-Object PSObject -Property $ComparedValue))
            } 
            ElseIf ($Result.SideIndicator -eq '<=') {
                $ComparedValue  = [ordered] @{
                    Trade = [string]$Result.InputObject
                }
                $null = $ExpectsReportedValues.Add($(New-Object PSObject -Property $ComparedValue))
            } 
            Else {
                Write-Host $($Result.SideIndicator) $($Result.InputObject)
            }
        }
        If ($MatchedValues) {
            $Content = ForEach ($value in $MatchedValues.Trade) {$value}
            $xml = $($Header),$($Content),$($Footer) 
            Set-Content -Path $MatchedFileNew -Value $xml
        } 
        If ($ActualReportedValues) {
            $Content = ForEach ($value in $ActualReportedValues.Trade) {$value}
            $xml = $($Header),$($Content),$($Footer) 
            Set-Content -Path $ActualFileNew -Value $xml
        }
        If ($ExpectsReportedValues) {
            $Content = ForEach ($value in $ExpectsReportedValues.Trade) {$value}
            $xml = $($Header),$($Content),$($Footer) 
            Set-Content -Path $ExpectsFileNew -Value $xml
        }
    }         
    End {
    }
} #Compare-XML-Files

You have the talent to obfuscate your goal for others. I did not get what property you actually want to sort by.

Assumed you want to sort the elements of the node Sct you can list them like this:

$ReferenceXML.Rt.CUDATE.Coll.SctLnd.Cosd.Asst | 
    Select-Object -ExpandProperty Sct | 
        Format-Table -AutoSize

The output looks like this:

Name        Type    NmnlV UP kktV Ql   Mtty       Dsr Pt HrMrgn
----        ----    ----- -- ---- --   ----       --- -- ------
GB123456789 DFGHJJK NmnlV UP kktV YUIU 2010-10-04 Dsr Pt 99
GB123456789 FFGHXX  NmnlV UP      OIUY 2023-10-04 Dsr Pt 99

!! I just added Format-Table to make the order more obvious. !!
!! Please note that there are only 10 of your 11 properties displayed. This is a limitation of Format-Table. But the properties are still there. !! :point_up:t3:

If you want to sort by the property Type you specify the property Type for the cmdlet Sort-Object:

$ReferenceXML.Rt.CUDATE.Coll.SctLnd.Cosd.Asst | 
    Select-Object -ExpandProperty Sct | 
      Sort-Object -Property Name

The output looks like this:

Name        Type    NmnlV UP kktV Ql   Mtty       Dsr Pt HrMrgn
----        ----    ----- -- ---- --   ----       --- -- ------
GB123456789 DFGHJJK NmnlV UP kktV YUIU 2010-10-04 Dsr Pt 99
GB123456789 FFGHXX  NmnlV UP      OIUY 2023-10-04 Dsr Pt 99 

Nothing changed actually. So now we sort for the property Ql.

$ReferenceXML.Rt.CUDATE.Coll.SctLnd.Cosd.Asst | 
    Select-Object -ExpandProperty Sct |  
        Sort-Object -Property Ql |
            Format-Table -AutoSize

Now the output shows the changed order:

Name        Type    NmnlV UP MktVal Ql   Mtty       Dsr Pt HrMrgn
----        ----    ----- -- ------ --   ----       --- -- ------
GB123456789 FFGHXX  NmnlV UP MktVal OIUY 2023-10-04 Dsr Pt 99
GB123456789 DFGHJJK NmnlV UP        YUIU 2010-10-04 Dsr Pt 99

How do you actually compare these objects? For the cmdlet Compare-Object the order does not matter!? :point_up:t3: :thinking:

I have added more code and a result that I would like to have the code do so I can proceed to the compare If statements.

I will not digg into that code, sorry.

I’m still not sure what you’re trying to compare. As I already mentioned before I’m not an expert with XML at all. I try to avoid it when I can. But since XML is a hierarchical data structure I’d expect it to be hard to compare and/or sort if you want to compare or sort different nodes on different depths/levels.

I showed you how to sort elements on the same level and that’s valid for every level. But you’d need to treat every level seperately.

That’s actually not a very good idea because it is hard to know what’s have been there before and what’s new. :wink:

Anyway … I’m not sure if PowerShell is the right tool for the job.

I showed in my answer how to sort by a particular property of elements from the same level. For comparisons it’s a kind of the same deal. You can easily compare elements on the same level by one or more particular properties. Now it’s up to you to decide if that’s what you need or not. :man_shrugging:t3:

Thank you for all the help.