core/ConvertTo-ObjectSortedProperties.ps1
|
#Requires -Version 7.0 <# .SYNOPSIS Recursively sorts object properties alphabetically for deterministic JSON output. .DESCRIPTION Walks nested objects, dictionaries, and arrays and returns an equivalent structure with object keys sorted alphabetically. Intended to normalize Graph API responses before serialization so backups are stable across runs. .PARAMETER InputObject The input object to normalize. #> function ConvertTo-ObjectSortedPropertiesInternal { param( $Value ) if ($null -eq $Value) { return $null } if ( $Value.GetType().IsPrimitive -or $Value -is [string] -or $Value -is [decimal] -or $Value -is [datetime] -or $Value -is [datetimeoffset] -or $Value -is [timespan] -or $Value -is [guid] -or $Value -is [enum] ) { return $Value } if ($Value -is [System.Collections.IDictionary]) { $sorted = [ordered]@{} foreach ($entry in ($Value.GetEnumerator() | Sort-Object { [string]$_.Key })) { $sorted[$entry.Key] = ConvertTo-ObjectSortedPropertiesInternal -Value $entry.Value } return $sorted } if ($Value -is [System.Collections.IEnumerable] -and $Value -isnot [string]) { $items = [System.Collections.Generic.List[object]]::new() foreach ($item in $Value) { $items.Add((ConvertTo-ObjectSortedPropertiesInternal -Value $item)) } return , $items.ToArray() } $properties = @($Value.PSObject.Properties | Where-Object { $_.MemberType -eq [System.Management.Automation.PSMemberTypes]::NoteProperty }) if ($properties.Count -gt 0) { $sortedObject = [ordered]@{} foreach ($property in ($properties | Sort-Object Name)) { $sortedObject[$property.Name] = ConvertTo-ObjectSortedPropertiesInternal -Value $property.Value } return [PSCustomObject]$sortedObject } return $Value } function ConvertTo-ObjectSortedProperties { [CmdletBinding()] param( [Parameter(ValueFromPipeline = $true)] [AllowNull()] $InputObject ) process { return (ConvertTo-ObjectSortedPropertiesInternal -Value $InputObject) } } Export-ModuleMember -Function ConvertTo-ObjectSortedProperties |