Source/Public/ConvertFrom-Expression.ps1
<#
.SYNOPSIS Deserializes a PowerShell expression to an object. .DESCRIPTION The `ConvertFrom-Expression` cmdlet safely converts a PowerShell formatted expression to an object-graph existing of a mixture of nested arrays, hashtables and objects that contain a list of strings and values. .PARAMETER InputObject Specifies the PowerShell expressions to convert to objects. Enter a variable that contains the string, or type a command or expression that gets the string. You can also pipe a string to ConvertFrom-Expression. The **InputObject** parameter is required, but its value can be an empty string. The **InputObject** value can't be `$null` or an empty string. .PARAMETER LanguageMode Defines which object types are allowed for the deserialization, see: [About language modes][2] * Any type that is not allowed by the given language mode, will be omitted leaving a bare `[ValueType]`, `[String]`, `[Array]` or `[HashTable]`. * Any variable that is not `$True`, `$False` or `$Null` will be converted to a literal string, e.g. `$Test`. > [!Caution] > > In full language mode, `ConvertTo-Expression` permits all type initializers. Cmdlets, functions, > CIM commands, and workflows will *not* be invoked by the `ConvertFrom-Expression` cmdlet. > > Take reasonable precautions when using the `Invoke-Expression -LanguageMode Full` command in scripts. > Verify that the class types in the expression are safe before instantiating them. In general, it is > best to design your configuration expressions with restricted or constrained classes, rather than > allowing full freeform expressions. .PARAMETER ArrayAs If supplied, the array subexpression `@( )` syntaxes without an type initializer or with an unknown or denied type initializer will be converted to the given list type. .PARAMETER HashTableAs If supplied, the array subexpression `@{ }` syntaxes without an type initializer or with an unknown or denied type initializer will be converted to the given map (dictionary or object) type. #> function ConvertFrom-Expression { [CmdletBinding(HelpUri='https://github.com/iRon7/ObjectGraphTools/blob/main/Docs/ConvertFrom-Expression.md')][OutputType([Object])] param( [Parameter(Mandatory = $true, ValueFromPipeLine = $True)] [Alias('Expression')][String]$InputObject, [ValidateScript({ $_ -ne 'NoLanguage' })] [System.Management.Automation.PSLanguageMode]$LanguageMode = 'Restricted', [ValidateNotNull()]$ArrayAs, [ValidateNotNull()]$HashTableAs ) begin { function StopError($Exception, $Id = 'IncorrectArgument', $Group = [Management.Automation.ErrorCategory]::SyntaxError, $Object){ if ($Exception -is [System.Management.Automation.ErrorRecord]) { $Exception = $Exception.Exception } elseif ($Exception -isnot [Exception]) { $Exception = [ArgumentException]$Exception } $PSCmdlet.ThrowTerminatingError([System.Management.Automation.ErrorRecord]::new($Exception, $Id, $Group, $Object)) } if ($this.LanguageMode -eq 'NoLanguage') { Throw 'The language mode "NoLanguage" is not supported.' } function NewNode($Object) { # Should become a New-Node cmdlet if ($Null -eq $Object) { return } elseif ($Object -is [String] -or $Object -is [Type]) { $String = "$Object" # This will use the type accessor for [Type] type. if ('Array', 'Object[]' -eq $String) { $Object = @() } elseif ('ordered', 'OrderedDictionary' -eq $String) { $Object = [System.Collections.Specialized.OrderedDictionary]::new([StringComparer]::OrdinalIgnoreCase) } elseif ('hashtable' -eq $String) { $Object = [HashTable]::new([StringComparer]::OrdinalIgnoreCase) } elseif ('psobject', 'PSCustomObject' -eq $String) { $Object = [PSCustomObject]@{} } else { $Type = $TypeName -as [Type] if (-not $Type) { StopError "Unknown type: [$Object]" } $Object = New-Object -Type $TypeName } } elseif ($Null -ne $Object.GetType().GetMethod('Clear')) { $Object.Clear() } [PSNode]::ParseInput($Object) } $ArrayNode = NewNode $ArrayAs $HashTableNode = NewNode $HashTableAs if ( $ArrayNode -is [PSMapNode] -and $HashTableNode -is [PSListNode] -or -not $ArrayNode -and $HashTableNode -is [PSListNode] -or $ArrayNode -is [PSMapNode] -and -not $HashTableNode ) { $ArrayNode, $HashTableNode = $HashTableNode, $ArrayNode # In case the parameter positions are swapped } $ArrayType = if ($ArrayNode) { if ($ArrayType -is [PSListNode]) { $ArrayNode.ValueType } else { StopError 'The -ArrayAs parameter requires a string, type or an object example that supports a list structure' } } $HashTableType = if ($HashTableNode) { if ($HashTableNode -is [PSMapNode]) { $HashTableNode.ValueType } else { StopError 'The -HashTableAs parameter requires a string, type or an object example that supports a map structure' } } if ('System.Management.Automation.PSCustomObject' -eq $HashTableNode.ValueType) { $HashTableType = 'PSCustomObject' -as [type] } # https://github.com/PowerShell/PowerShell/issues/2295 } process { [PSDeserialize]::new($InputObject, $LanguageMode, $ArrayType, $HashTableType).Object } } |