Public/ConvertTo-Yaml.ps1
function ConvertTo-Yaml { <# .SYNOPSIS creates a YAML description of the data in the object .DESCRIPTION This produces YAML from any object you pass to it. It isn't suitable for the huge objects produced by some of the cmdlets such as Get-Process, but fine for simple objects .EXAMPLE $array=@() $array+=Get-Process wi* | Select-Object Handles,NPM,PM,WS,VM,CPU,Id,ProcessName ConvertTo-YAML $array .PARAMETER Object the object that you want scripted out .PARAMETER Depth The depth that you want your object scripted to .PARAMETER Nesting Level internal use only. required for formatting #> [OutputType('System.String')] [CmdletBinding()] param ( [parameter(Position = 0, Mandatory = $true, ValueFromPipeline = $true)] [AllowNull()] $inputObject, [parameter(Position = 1, Mandatory = $false, ValueFromPipeline = $false)] [int]$depth = 16, [parameter(Position = 2, Mandatory = $false, ValueFromPipeline = $false)] [int]$NestingLevel = 0, [parameter(Position = 3, Mandatory = $false, ValueFromPipeline = $false)] [int]$XMLAsInnerXML = 0 ) BEGIN { } PROCESS { # if it is null return null If ( !($inputObject) ) { return '' } if ($NestingLevel -eq 0) {$Output= "---`r`n" } else {$output=''} $padding = [string]' ' * $NestingLevel # lets just create our left-padding for the block try { $Type = $inputObject.GetType().Name # we start by getting the object's type if ($Type -ieq 'Object[]') { #what it really is $Type = "$($inputObject.GetType().BaseType.Name)" } #report the leaves in terms of object type if ($depth -ilt $NestingLevel) { $Type = 'OutOfDepth' } elseif ($Type -ieq 'XmlDocument' -or $Type -ieq 'XmlElement') { if ($XMLAsInnerXML -ne 0) { $Type = 'InnerXML' } else { $Type = 'XML' } } # convert to PS Alias # prevent these values being identified as an object if (@('boolean', 'byte', 'byte[]', 'char', 'datetime', 'decimal', 'double', 'float', 'single', 'guid', 'int', 'int32', 'int16', 'long', 'int64', 'OutOfDepth', 'RuntimeType', 'PSNoteProperty', 'regex', 'sbyte', 'string', 'timespan', 'uint16', 'uint32', 'uint64', 'uri', 'version', 'void', 'xml', 'datatable', 'Dictionary`2', 'SqlDataReader', 'datarow', 'ScriptBlock', 'type') -notcontains $type) { if ($Type -ieq 'OrderedDictionary') { $Type = 'HashTable' } elseif ($Type -ieq 'PSCustomObject') { $Type = 'PSObject' } elseif ($Type -ieq 'List`1') { $Type = 'Array' } elseif ($inputObject -is "Array") { $Type = 'Array' } # whatever it thinks it is called elseif ($inputObject -is "HashTable") { $Type = 'HashTable' } # for our purposes it is a hashtable elseif (!($inputObject | Get-Member -membertype Properties | Select-Object name | Where-Object name -like 'Keys')) { $Type = 'generic' } #use dot notation elseif (($inputObject | Get-Member -membertype Properties | Select-Object name).count -gt 1) { $Type = 'Object' } } write-verbose "$($padding)Type:='$Type', Object type:=$($inputObject.GetType().Name), BaseName:=$($inputObject.GetType().BaseType.Name) " $output += switch ($Type) { 'ScriptBlock'{ "{$($inputObject.ToString())}" } 'InnerXML' { "|`r`n" + ($inputObject.OuterXMl.Split("`r`n") | ForEach-Object{ "$padding$_`r`n" }) } 'DateTime' { $inputObject.ToString('s') } # s=SortableDateTimePattern (based on ISO 8601) using local time 'Byte[]' { $string = [System.Convert]::ToBase64String($inputObject) if ($string.Length -gt 100) { # right, we have to format it to YAML spec. '!!binary "\' + "`r`n" # signal that we are going to use the readable Base64 string format #$bits = @() $length = $string.Length $IndexIntoString = 0 $wrap = 100 while ($length -gt $IndexIntoString + $Wrap) { $padding + $string.Substring($IndexIntoString, $wrap).Trim() + "`r`n" $IndexIntoString += $wrap } if ($IndexIntoString -lt $length) { $padding + $string.Substring($IndexIntoString).Trim() + "`r`n" } else { "`r`n" } } else { '!!binary "' + $($string -replace '''', '''''') + '"' } } 'Boolean' { "$(&{ if ($inputObject -eq $true) { 'true' } else { 'false' } })" } 'string' { $String = "$inputObject" if ($string -match '[\r\n]' -or $string.Length -gt 80) { # right, we have to format it to YAML spec. $folded = ">`r`n" # signal that we are going to use the readable 'newlines-folded' format $string.Split("`n") | ForEach-Object { $_=$_ -replace "\r$" $length = $_.Length $IndexIntoString = 0 $wrap = 80 while ($length -gt $IndexIntoString + $Wrap) { $BreakPoint = $wrap $earliest = $_.Substring($IndexIntoString, $wrap).LastIndexOf(' ') $latest = $_.Substring($IndexIntoString + $wrap).IndexOf(' ') if (($earliest -eq -1) -or ($latest -eq -1)) { $BreakPoint = $wrap } elseif ($wrap - $earliest -lt ($latest)) { $BreakPoint = $earliest } else { $BreakPoint = $wrap + $latest } if (($wrap - $earliest) + $latest -gt 30) { $BreakPoint = $wrap # in case it is a string without spaces } $folded += $padding + $_.Substring($IndexIntoString, $BreakPoint).Trim() + "`r`n" $IndexIntoString += $BreakPoint } if ($IndexIntoString -lt $length) { $folded += $padding + $_.Substring($IndexIntoString).Trim() + "`r`n" } else { $folded += "`r`n" } } $folded } else { "'$($string -replace '''', '''''')'" } } 'Char' { "([int]$inputObject)" } { @('byte', 'decimal', 'double', 'float', 'single', 'int', 'int32', 'int16', ` 'long', 'int64', 'sbyte', 'uint16', 'uint32', 'uint64') -contains $_ } { "$inputObject" } # rendered as is without single quotes 'PSNoteProperty' { "$(ConvertTo-YAML -inputObject $inputObject.Value -depth $depth -NestingLevel ($NestingLevel + 1))" } 'Array' { "$($inputObject | Foreach-Object { "`r`n$padding- $(ConvertTo-YAML -inputObject $_ -depth $depth -NestingLevel ($NestingLevel + 1))" })" } 'HashTable'{ ("$($inputObject.GetEnumerator() | Foreach-Object { if ($_.Value.GetType().Name -eq 'String'){$increment=2} else {$Increment=1} "`r`n$padding $($_.Name): " + (ConvertTo-YAML -inputObject $_.Value -depth $depth -NestingLevel ($NestingLevel + $increment)) })") } 'Dictionary`2'{ ("$($inputObject.GetEnumerator() | Foreach-Object { "`r`n$padding $($_.Key): " + (ConvertTo-YAML -inputObject $_.Value -depth $depth -NestingLevel ($NestingLevel + 1)) })") } 'PSObject' { ("$($inputObject.PSObject.Properties | Foreach-Object { "`r`n$padding $($_.Name): " + (ConvertTo-YAML -inputObject $_ -depth $depth -NestingLevel ($NestingLevel + 1)) })") } 'generic' { "$($inputObject.Keys | Foreach-Object { "`r`n$padding $($_): $(ConvertTo-YAML -inputObject $inputObject.$_ -depth $depth -NestingLevel ($NestingLevel + 1))" })" } 'Object' { ("$($inputObject | Get-Member -membertype properties | Select-Object name | Foreach-Object { "`r`n$padding $($_.name): $(ConvertTo-YAML -inputObject $inputObject.$($_.name) -depth $NestingLevel -NestingLevel ($NestingLevel + 1))" })") } 'XML' { ("$($inputObject | Get-Member -membertype properties | Where-Object { @('xml', 'schema') -notcontains $_.name } | Select-Object name | Foreach-Object { "`r`n$padding $($_.name): $(ConvertTo-YAML -inputObject $inputObject.$($_.name) -depth $depth -NestingLevel ($NestingLevel + 1))" })") } 'DataRow' { ("$($inputObject | Get-Member -membertype properties | Select-Object name | Foreach-Object { "`r`n$padding $($_.name): $(ConvertTo-YAML -inputObject $inputObject.$($_.name) -depth $depth -NestingLevel ($NestingLevel + 1))" })") } <# 'SqlDataReader'{ $all = $inputObject.FieldCount while ($inputObject.Read()) {for ($i = 0; $i -lt $all; $i++) {"`r`n$padding $($Reader.GetName($i)): $(ConvertTo-YAML -inputObject $($Reader.GetValue($i)) -depth $depth -NestingLevel ($NestingLevel+1))"}} #> default { "'$inputObject'" } } return $Output } catch { write-error "Error'$($_)' in script $($_.InvocationInfo.ScriptName) $($_.InvocationInfo.Line.Trim()) (line $($_.InvocationInfo.ScriptLineNumber)) char $($_.InvocationInfo.OffsetInLine) executing $($_.InvocationInfo.MyCommand) on $type object '$($inputObject)' Class: $($inputObject.GetType().Name) BaseClass: $($inputObject.GetType().BaseType.Name) " } finally { } } END { } } |