TypeHelper.psm1
function ConvertTo-String { [CmdletBinding()] param ( [Parameter(Mandatory=$true)] [ValidateScript({ If ('Hashtable','OrderedDictionary','AXNodeConfiguration','PSBoundParametersDictionary' -contains $_.GetType().Name) { $true } else { throw "Supported InputTypes are 'Hashtable' and 'OrderedDictionary'" } })] $InputObject, [Parameter(Mandatory=$false)] [switch]$DoNotFormat ) Begin { function priv_Escape-SpecialChars { param ( [Parameter(Mandatory=$true, Position = 0)] [AllowEmptyString()] [string]$InputObject ) if([string]::IsNullOrEmpty($InputObject)) { return "" } else { [string]$ParsedText = $InputObject if($ParsedText.ToCharArray() -icontains "'") { $ParsedText = $ParsedText -replace "'","''" } return $ParsedText } } } Process { $sb = new-object System.Text.StringBuilder $null = $sb.AppendLine('@{') foreach ($key in $InputObject.Keys) { if ($InputObject[$key]) { switch ($inputObject[$key].GetType().Name) { ### ScriptBlocks 'ScriptBlock' { $null = $sb.AppendLine("$key = `{$($inputObject[$key].ToString())`}") break } ### Strings and Enums { @('String','ActionPreference') -contains $_ } { [string]$itemText = "{0} = '{1}'" -f "$key", $(priv_Escape-SpecialChars -InputObject $inputObject[$key]) $null = $sb.AppendLine($itemText) break } ### String Arrays 'String[]' { [string]$itemText = "{0} = @({1})" -f "$key", "$($($inputObject[$key] | foreach { "'$(priv_Escape-SpecialChars -InputObject $_)'" }) -join ", ")" $null = $sb.AppendLine($itemText) break } ### Numerics { ($_ -ilike '*int*') -or (@('single','double','decimal','SByte','Byte') -icontains $_) } { [string]$itemText = "{0} = {1}" -f "$key", $($inputObject[$key]).ToString() $null = $sb.AppendLine($itemText) break } ### Nested Hashtables (recursive call) {'Hashtable','OrderedDictionary','PSBoundParametersDictionary' -contains $_} { [string]$itemText = "{0} = {1}" -f "$key", $(ConvertTo-String -InputObject $inputObject[$key] -DoNotFormat) $null = $sb.AppendLine($itemText) break } ### Nested Hashtable Arrays (recursive call) {'Hashtable[]','OrderedDictionary[]','PSBoundParametersDictionary[]' -contains $_} { $NewLineStr = [Environment]::NewLine $JoinSeparator = ",$NewLineStr" [string]$itemText = "{0} = @($NewLineStr{1}$NewLineStr)" -f "$key", "$($($inputObject[$key] | foreach { ConvertTo-String -InputObject $_ -DoNotFormat }) -join $JoinSeparator)" $null = $sb.AppendLine($itemText) break } ### Booleans and Switches { @('Boolean','SwitchParameter') -contains $_ } { [string]$itemText = '{0} = ${1}' -f "$key", $($inputObject[$key].ToString()) $null = $sb.AppendLine($itemText) break } ### PSCustomObject (NoteProperties only) 'PSCustomObject' { # Convert to hashtable $propHash = @{} foreach ($prop in $inputObject[$key].PSObject.Properties) { $propHash[$prop.Name] = $prop.Value } [string]$itemText = '{0} = $([PSCustomObject] {1})' -f "$key", $(ConvertTo-String -InputObject $propHash -DoNotFormat) $null = $sb.AppendLine($itemText) break } ### DateTime 'DateTime' { [string]$itemText = "{0} = '{1}'" -f "$key", $($inputObject[$key].ToUniversalTime().ToString("dd.MM.yyyy HH.mm:ss UTC", [CultureInfo]::InvariantCulture)) $null = $sb.AppendLine($itemText) break } Default { Write-Warning "Serializing not supported key: $key that contains: $_" [string]$itemText = '{0} = {1}' -f "$key", $($inputObject[$key].ToString()) $null = $sb.AppendLine($itemText) } } } else { $null = $sb.AppendLine('{0} = $null' -f "$key") } } $null = $sb.AppendLine('}') $result = $sb.ToString() if($DoNotFormat.IsPresent) { $result.Trim([environment]::NewLine) } else { ConvertTo-TabifiedString -ScriptText $result } } End { } } function ConvertTo-Hashtable { param ( [ValidateScript({ $TempParam = $_ switch ($TempParam.GetType().Fullname) { 'System.String' { try { $obj = ConvertFrom-Json -InputObject $TempParam -ErrorAction Stop $Script:InputObjectData = $obj.psobject.Properties } catch { throw "InputObject is not a valid json string" } break } default { $Script:InputObjectData = $TempParam.psobject.Properties } } $true })] $InputObject ) begin { $DepthThreshold = 32 function Get-IOProperty { param ( [Parameter(Mandatory=$true)] [System.Management.Automation.PSPropertyInfo[]]$Property, [Parameter(Mandatory=$true)] [int]$CurrentDepth ) #Increse and chech Depth $CurrentDepth++ if ($Function:Depth -ge $DepthThreshold) { Write-Error -Message "Converting to Hashtable reached Depth Threshold of 32 on $($Property.Name -join ',')" -ErrorAction Stop } $Ht = [hashtable]@{} foreach ($Prop in $Property) { if ($Prop.Value) { switch ($Prop.TypeNameOfValue) { 'System.String' { $ht.Add($Prop.Name,$Prop.Value) break } 'System.Boolean' { $ht.Add($Prop.Name,$Prop.Value) break } 'System.DateTime' { $ht.Add($Prop.Name,$Prop.Value.ToString()) break } {$_ -ilike '*int*'} { $ht.Add($Prop.Name,$Prop.Value) break } default { $ht.Add($Prop.Name,(Get-IOProperty -Property $Prop.Value.psobject.Properties -CurrentDepth $CurrentDepth)) } } } else { $ht.Add($Prop.Name,$null) } } $Ht } } process { $CurrentDepth = 0 Get-IOProperty -Property $InputObjectData -CurrentDepth $CurrentDepth } end { } } function ConvertTo-TabifiedString { [CmdletBinding()] Param ( $ScriptText ) $CurrentLevel = 0 $ParseError = $null $Tokens = $null $AST = [System.Management.Automation.Language.Parser]::ParseInput($ScriptText, [ref]$Tokens, [ref]$ParseError) if($ParseError) { $ParseError | Write-Error throw 'The parser will not work properly with errors in the script, please modify based on the above errors and retry.' } for($t = $Tokens.Count -2; $t -ge 1; $t--) { $Token = $Tokens[$t] $NextToken = $Tokens[$t-1] if ($token.Kind -match '(L|At)Curly') { $CurrentLevel-- } if ($NextToken.Kind -eq 'NewLine' ) { # Grab Placeholders for the Space Between the New Line and the next token. $RemoveStart = $NextToken.Extent.EndOffset $RemoveEnd = $Token.Extent.StartOffset - $RemoveStart $tabText = "`t" * $CurrentLevel $ScriptText = $ScriptText.Remove($RemoveStart,$RemoveEnd).Insert($RemoveStart,$tabText) } if ($token.Kind -eq 'RCurly') { $CurrentLevel++ } } $ScriptText } function Resolve-ObjectProperty { [CmdletBinding()] [OutputType([object])] param ( #InputObject [Parameter(Mandatory=$true,ParameterSetName='NoRemoting_Default')] [object[]]$InputObject, #PropertyName [Parameter(Mandatory=$true,ParameterSetName='NoRemoting_Default')] [string]$PropertyName, #PropertyValueReference [Parameter(Mandatory=$true,ParameterSetName='NoRemoting_Default')] [hashtable]$PropertyValueReference ) Begin { } Process { foreach ($object in $InputObject) { if ($object.psobject.Properties.Name -contains $PropertyName) { if ($PropertyValueReference.ContainsKey(($object.$PropertyName))) { $object.$PropertyName = $PropertyValueReference[$object.$PropertyName] } } } $InputObject } End { } } function Import-PSDataFile { [CmdletBinding()] Param ( [Parameter(Mandatory)] [Microsoft.PowerShell.DesiredStateConfiguration.ArgumentToConfigurationDataTransformation()] [hashtable] $FilePath ) return $FilePath } function ConvertFrom-JsonString { [CmdletBinding()] param ( #InputObject [Parameter(Mandatory=$true,ParameterSetName='NoRemoting_Default')] $InputObject ) Begin { } Process { add-type -assembly system.web.extensions $ps_js = New-Object system.web.script.serialization.javascriptSerializer -ErrorAction Stop $ps_js.DeserializeObject($InputObject) | foreach { New-Object -TypeName psobject -Property $_ -ErrorAction Stop } } End { } } function New-DynamicConfiguration { [CmdletBinding()] [OutputType([void])] param ( #Definition [Parameter(Mandatory=$true,Position=0,ParameterSetName='NoRemoting_Default')] [scriptblock]$Definition ) Begin { } process { $blockDefinition = $Definition.ToString() + "`n" + 'Export-ModuleMember -Variable *' $result = . New-Module -AsCustomObject -ScriptBlock ([scriptblock]::Create($blockDefinition)) $SubProperties = $result.psobject.Properties | Where-Object {$_.TypeNameOfValue -eq 'System.Management.Automation.ScriptBlock'} -ErrorAction Stop foreach ($item in $SubProperties) { $result."$($item.Name)" = New-DynamicConfiguration -Definition $result."$($item.Name)" -ErrorAction Stop } $result } End { } } Function Get-DerivedType { [CmdletBinding()] param ( [Parameter(Mandatory=$true)] [string]$BaseType, [Parameter(Mandatory=$true)] [ValidateSet('AppDomain','File')] [string]$Scope, [Parameter(Mandatory=$false)] [switch]$Recurse ) DynamicParam { #Assembly $Assembly_AttrColl = new-object -Type System.Collections.ObjectModel.Collection[System.Attribute] $Assembly_Param = new-object -Type System.Management.Automation.RuntimeDefinedParameter('Assembly',[string[]],$Assembly_AttrColl) if ($Scope -eq 'AppDomain') { $Assembly_Attr1 = new-object System.Management.Automation.ParameterAttribute $Assembly_Attr1.Mandatory = $false $Assembly_Param.Attributes.Add($Assembly_Attr1) $Assembly_Attr2 = New-Object System.Management.Automation.ValidateSetAttribute -ArgumentList ([System.AppDomain]::CurrentDomain.GetAssemblies().FullName) $Assembly_Param.Attributes.Add($Assembly_Attr2) } else { $Assembly_Attr1 = new-object System.Management.Automation.ParameterAttribute $Assembly_Attr1.Mandatory = $true $Assembly_Param.Attributes.Add($Assembly_Attr1) } $DynamicParams = new-object -Type System.Management.Automation.RuntimeDefinedParameterDictionary $DynamicParams.Add('Assembly',$Assembly_Param) $DynamicParams } begin { Function priv_Resolve-DerivedType { [CmdletBinding()] param ( [Parameter(Mandatory=$true)] [string]$BaseType, [Parameter(Mandatory=$true)] [System.Reflection.Assembly[]]$Assembly, [Parameter(Mandatory=$false)] [switch]$Recurse ) process { $Assembly.ExportedTypes | foreach { if ($_.BaseType.FullName -eq $BaseType) { $_ if ($Recurse.IsPresent) { priv_Resolve-DerivedType -BaseType $_.FullName -Assembly $Assembly -Recurse:$Recurse.IsPresent } } } } } } process { if ($Scope -eq 'AppDomain' -and $PSBoundParameters.ContainsKey('Assembly')) { $ResolvedAssembly = [System.Reflection.Assembly]::Load($PSBoundParameters['Assembly']) } else { $ResolvedAssembly = [System.AppDomain]::CurrentDomain.GetAssemblies() } priv_Resolve-DerivedType -BaseType $BaseType -Recurse:$Recurse.IsPresent -Assembly $ResolvedAssembly } end { } } function Get-Version { [CmdletBinding()] [OutputType([System.Version])] param ( #InputObject [Parameter(Mandatory=$true,ParameterSetName='NoRemoting_Default')] [string]$InputObject ) Begin { } Process { [ref]$Version = $null if ([System.Version]::TryParse($InputObject,$Version)) { if ($Version.Value.Revision -eq -1) { $Revision = 0 } else { $Revision = $Version.Value.Revision } if ($Version.Value.Build -eq -1) { $Build = 0 } else { $Build = $Version.Value.Build } } else { throw "$InputObject cannot be parsed as version" } [System.Version]::new($Version.Value.Major,$Version.Value.Minor,$Build,$Revision) } End { } } function Update-HTMLSpecialChars { [CmdletBinding()] [OutputType([string])] param ( #HTML String to format [Parameter(Mandatory=$true,ParameterSetName='NoRemoting_Default')] [string]$StringAsHTML ) Begin { } Process { try { $StringAsHTML = $StringAsHTML.Replace("�","Ä") #Capital A-umlaut $StringAsHTML = $StringAsHTML.Replace("�","ä") #Lowercase a-umlaut $StringAsHTML = $StringAsHTML.Replace("�","É") #Lowercase a-umlaut $StringAsHTML = $StringAsHTML.Replace("�","é") #Lowercase E-acute $StringAsHTML = $StringAsHTML.Replace("�","Ö") #Capital O-umlaut $StringAsHTML = $StringAsHTML.Replace("�","ö") #Lowercase o-umlaut $StringAsHTML = $StringAsHTML.Replace("�","Ü") #Capital U-umlaut $StringAsHTML = $StringAsHTML.Replace("�","ü") #Lowercase u-umlaut $StringAsHTML = $StringAsHTML.Replace("�","ß") #SZ ligature $StringAsHTML = $StringAsHTML.Replace("�","«") #Left angle quotes $StringAsHTML = $StringAsHTML.Replace("�","»") #Right angle quotes $StringAsHTML = $StringAsHTML.Replace('�',"„") #Left lower quotes $StringAsHTML = $StringAsHTML.Replace('�',"“") #Left quotes $StringAsHTML = $StringAsHTML.Replace('�',"”") #Right quotes $StringAsHTML = $StringAsHTML.Replace("�","°") #Degree sign (Grad) $StringAsHTML = $StringAsHTML.Replace("�","€") #Euro $StringAsHTML = $StringAsHTML.Replace("�","£") #Pound Sterling return $StringAsHTML } catch { throw "Special chars could not be replaced for string: $string" } } End { } } function Test-JsonSchema { [CmdletBinding()] [OutputType([bool])] param ( #InputObject [Parameter(Mandatory=$true,ParameterSetName='NoRemoting_Default')] [string]$InputObject, #Schema [Parameter(Mandatory=$true,ParameterSetName='NoRemoting_Default')] [string]$Schema, #ValidationMessage [Parameter(Mandatory=$false,ParameterSetName='NoRemoting_Default')] [ref]$ValidationMessage ) Begin { } Process { $JSchema = [Newtonsoft.Json.Schema.JSchema]::Parse($Schema) $JObject = [Newtonsoft.Json.Linq.JObject]::Parse($InputObject) if ($PSBoundParameters.ContainsKey('ValidationMessage')) { [Newtonsoft.Json.Schema.SchemaExtensions]::IsValid($JObject,$JSchema,$ValidationMessage) } else { [Newtonsoft.Json.Schema.SchemaExtensions]::IsValid($JObject,$JSchema) } } End { } } function Split-Object { [CmdletBinding()] param ( #ByProperty [Parameter(Mandatory = $true, ParameterSetName = 'ByProperty')] [string]$ByProperty, #InputObject [Parameter(Mandatory = $true, ParameterSetName = 'ByProperty')] [object[]]$InputObject, #ChunkSize [Parameter(Mandatory = $true)] [int]$ChunkSize ) begin { $ioEndIdx = 0 $o = 0 } process { for ($ioStartIdx = 0; $ioStartIdx -lt $InputObject.Count; $ioStartIdx = $ioEndIdx + 1) { # Calculate InputObject End Index ($ioEndIdx) for ($ioEndIdxCandidate = $ioStartIdx; $ioEndIdxCandidate -lt $InputObject.Count; $ioEndIdxCandidate++) { $curSize = ($InputObject[$ioStartIdx..$ioEndIdxCandidate]."$ByProperty".Count | Measure-Object -Sum).Sum $ioEndIdx = [math]::Max($ioStartIdx, $ioEndIdxCandidate - 1) if ($curSize -gt $ChunkSize) { break; } } # Check if a single InputObject item contains a block larger than the ChunkSize if (($ioStartIdx -eq $ioEndIdx) -and ($curSize -gt $ChunkSize)) { $part = 0 #Split the Object into multiple chunks for ($ioPropStartIdx = 0; $ioPropStartIdx -lt $InputObject[$ioStartIdx]."$ByProperty".Count; $ioPropStartIdx = $ioPropStartIdx + $ChunkSize) { $ioClone = $InputObject[$ioStartIdx] | ConvertTo-Json -Compress -Depth 10 -ErrorAction Stop | ConvertFrom-Json -ErrorAction Stop $ioClone."$ByProperty" = $InputObject[$ioStartIdx]."$ByProperty"[$ioPropStartIdx..($ioPropStartIdx + $ChunkSize -1)] [pscustomobject]@{ ChunkId = $o Part = $part Object = $ioClone } $part++ } } else { [pscustomobject]@{ ChunkId = $o Part = 0 Object = $InputObject[$ioStartIdx..$ioEndIdx] } } $o++ } } } function Merge-Object { [CmdletBinding()] param ( #ByProperty [Parameter(Mandatory = $true, ParameterSetName = 'ByProperty')] [string]$ByProperty, #InputObject [Parameter(Mandatory = $true, ParameterSetName = 'ByProperty')] [object[]]$InputObject ) process { $InputObject | Where-Object {$_.Part -eq 0} | foreach { $result = $_ | ConvertTo-Json -Compress -Depth 10 -ErrorAction Stop | ConvertFrom-Json $OtherParts = $InputObject | Where-Object {$_.ChunkId -eq $result.ChunkId} if (($OtherParts | Measure-Object).Count -gt 1) { $result.Object."$ByProperty" = $OtherParts.Object."$ByProperty" } $result.Object } } } function Set-JsonSchemaLicense { [CmdletBinding()] [OutputType([void])] param ( #LicensseKey [Parameter(Mandatory=$true,ParameterSetName='NoRemoting_Default')] [string]$LicensseKey ) Begin { } Process { [Newtonsoft.Json.Schema.License]::RegisterLicense($LicensseKey) } End { } } |