Commands/FormattingExtended/Format-Hashtable.ps1
function Format-Hashtable { <# .Synopsis Takes an creates a script to recreate a hashtable .Description Allows you to take a hashtable and create a hashtable you would embed into a script. Handles nested hashtables and indents nested hashtables automatically. .Example # Corrects the presentation of a PowerShell hashtable [Ordered]@{Foo='Bar';Baz='Bing';Boo=@{Bam='Blang'}} | Format-Hashtable .Outputs [string] .Outputs [ScriptBlock] .Link about_hash_tables #> [OutputType([string], [ScriptBlock])] [Management.Automation.Cmdlet("Format", "Object")] [ValidateScript({return $true})] param( # The hashtable or PSObject that will be written as a PowerShell Hashtable [Parameter(Position=0,ValueFromPipeline,ValueFromPipelineByPropertyName)] [PSObject] $InputObject, # Returns the content as a script block, rather than a string [switch]$AsScriptBlock, # If set, will return the hashtable and all nested hashtables as custom objects. [switch]$AsPSObject, # If set, items in the hashtable will be sorted alphabetically [Switch]$Sort, # If set, credentials will be expanded out into a hashtable containing the username and password. [Switch]$ExpandCredential, # If set, the outputted hashtable will not contain any extra whitespace. [switch]$Compress, # If set, will embed ScriptBlocks as literal strings, # so that the resulting hashtable could work in data language mode. [switch]$Safe, # The maximum depth to enumerate. # Beneath this depth, items will simply be returned as $null. [int]$Depth ) begin { $myCmd = $MyInvocation.MyCommand $myScriptBlock = $myCmd.ScriptBlock } process { if (-not $Compress) { $psCallstack = @(Get-PSCallStack) $callstack = @(foreach ($cs in $psCallstack) { if ($cs.InvocationInfo.MyCommand.ScriptBlock -eq $myCmd.ScriptBlock) { $cs } }) } else { $callstack = @() } $myParams = @{} + $PSBoundParameters $myParams.Remove('InputObject') $CurrentDepth = $callStack.Count if ($Depth -and $CurrentDepth -gt $Depth) { return '$null' } if ($inputObject -and $inputObject -isnot [Collections.IDictionary] -and $inputObject -isnot [string] -and (-not $InputObject.GetType().IsPrimitive) -and $InputObject -isnot [Enum] ) { $newInputObject = [Ordered]@{ PSTypeName=@($inputobject.pstypenames)[0] } if ('System.Object', 'System.Management.Automation.PSCustomObject' -contains $newInputObject.pstypename) { $newInputObject.Remove('PSTypeName') } foreach ($prop in $inputObject.psobject.properties) { $newInputObject[$prop.Name] = $prop.Value } $inputObject = $newInputObject } $scriptString = "" if ($inputObject -is [Collections.IDictionary]) { #region Indent $indent = $CurrentDepth * 4 $scriptString+= if ($Compress) { "@{" } else { "@{ " } #endregion Indent #region Include $items = $inputObject.GetEnumerator() if ($Sort) { $items = $items | Sort-Object Key } foreach ($kv in $items) { if (-not $Compress) { $scriptString+=" " * $indent } if ($kv.Key -eq 'keywords') { $null = $null } $keyString = "$($kv.Key)" if ($keyString.IndexOfAny(" _.#-+:;()'!?^@#$%&=|".ToCharArray()) -ne -1) { if ($keyString.IndexOf("'") -ne -1) { $scriptString+="'$($keyString.Replace("'","''"))' = " } else { $scriptString+="'$keyString' = " } } elseif ($keyString) { $scriptString+="$keyString = " } $value = $kv.Value $value = if ($value -is [string]) { "'" + $value.Replace("'","''").Replace("’", "’’").Replace("‘", "‘‘") + "'" } elseif ( $value -is [ScriptBlock] -or $value -is [Management.Automation.Language.Ast] ) { if ($safe) { "@'" + [Environment]::NewLine + $value [Environment]::NewLine + "'@'" } else { "{$value}" } } elseif ($value -is [switch]) { if ($value) { '$true' } else { '$false' } } elseif ($value -is [DateTime]) { "[DateTime]'$($value.ToString("o"))'" } elseif ($value -is [Timespan]) { if ($Safe) { "'$($value.ToString())'" } else { "[Timespan]'$($value.ToString())'" } } elseif ($value -is [bool]) { if ($value) { '$true'} else { '$false' } } elseif ($value -and $value.GetType -and ( $value.GetType().IsArray -or $value -is [Collections.IList] -or ($value -is [Collections.IEnumerable] -and $value -isnot [Collections.IDictionary]) )) { $joiner = if ($Compress) { ',' } else { "," + [Environment]::NewLine + (' ' * ($indent + 4)) } @(foreach ($v in $value) { if ($v -is [Collections.IDictionary]) { & $myScriptBlock -InputObject $v @myParams } elseif ($v -is [ScriptBlock] -or $v -is [Management.Automation.Language.Ast]) { if ($Safe) { } else { "{$v}" } } elseif ($v -is [Object] -and $v -isnot [string]) { & $myScriptBlock -InputObject $v @myParams } elseif ($v -is [bool] -or $v -is [switch]) { "`$$v" } elseif ($null -ne ($v -as [float])) { $v } else { ("'" + "$v".Replace("'","''").Replace("’", "’’").Replace("‘", "‘‘") + "'") } }) -join $joiner } elseif ($value -as [Collections.IDictionary[]]) { @(foreach ($v in $value) { & $myScriptBlock $v @myParams }) -join "," } elseif ($value -is [Collections.IDictionary]) { "$(& $myScriptBlock $value @myParams)" } elseif ($value -as [Double]) { "$value" } elseif ($value -is [Management.Automation.PSCredential] -and $ExpandCredential) { & $myScriptBlock -InputObject ([Ordered]@{ Username = $value.Username Password = $value.GetNetworkCredential().Password }) @myParams } else { $valueString = "'$($value -replace "'", "''")'" if ($valueString[0] -eq "'" -and $valueString[1] -eq "@" -and $valueString[2] -eq "{") { & $myScriptBlock -InputObject $value @myParams } else { $valueString } } $scriptString+= if ($Compress) { "$value;" } else { "$value" + [Environment]::NewLine } } if (-not $Compress) { $scriptString += " " * ($CurrentDepth - 1) * 4 } $scriptString += "}" if ($AsPSObject -and -not $Safe) { $scriptString = "[PSCustomObject][Ordered]$ScriptString" } #endregion Include } elseif ($InputObject -is [string]) { $scriptString = "'" + $InputObject.Replace("'", "''") + "'" } elseif ( $InputObject.GetType -and $InputObject.GetType().IsPrimitive ) { $scriptString += "$inputObject" } elseif ($inputObject -is [Enum]) { $scriptString = if ($safe) { "'" + $InputObject.ToString().Replace("'", "''") + "'" } else { "[$($InputObject.GetType().FullName -replace '^System\.')]" + "'" + $InputObject.ToString().Replace("'", "''") + "'" } } elseif (-not $inputObject) { $scriptString += '$null' } if ($AsScriptBlock) { [ScriptBlock]::Create($scriptString) } else { $scriptString } } } |