Search code examples
powershellcastingselect-object

PoweShell: Casting the data type for properties in Select-Object


I'm looking for a more efficient method (for both typing time and performance) of casting data types during a PowerShell Select-Object.

Currently, I am wrapping each individual property in an expression to cast the data type. I'm confident this isn't the correct way to do this, it just feels dirty...

The reason I am doing this is that I'm sending the data to a REST API which is applying strict validation using a JSON schema. The data in $Data is unreliable. For example, a property is sometimes a JSON string "12345" and occasionally an unexpected JSON Integer 12345.

The REST API then returns a 403 error because it was not expecting an Integer for that key.

$Results = $Data | select ` 
    @{Name = 'Name'; expression = {[string]$_.DisplayName}}, 
    @{Name = 'Version'; expression = {[string]$_.DisplayVersion}},  
    @{Name = 'HelpLink'; expression = {[string]$_.HelpLink}}, 
    @{Name = 'InstallLocation'; expression = {[string]$_.InstallLocation}}, 
    @{Name = 'InstallSource'; expression = {[string]$_.InstallSource}}, 
    @{Name = 'Language'; expression = {[int]$_.Language}},  
    @{Name = 'DisplayIcon'; expression = {[string]$_.DisplayIcon}}, 
    @{Name = 'UninstallString'; expression = {[string]$_.UninstallString}}, 
    @{Name = 'WindowsInstaller'; expression = {[int]$_.WindowsInstaller}},
    @{Name = 'AppGUID'; expression = {[string]$_.APP_GUID}},  
    @{Name = 'URLInfoAbout'; expression = {[string]$_.URLInfoAbout}}, 
    @{Name = 'Vendor'; expression = {[string]$_.Publisher}}, 
    @{Name = 'InstallDate'; expression = {[int]$_.InstallDate}},
    @{Name = 'EstimatedSize'; expression = {[int]$_.EstimatedSize}},
    @{Name = 'VersionMajor'; expression = {[string]$_.VersionMajor}},
    @{Name = 'VersionMinor'; expression = {[string]$_.VersionMinor}},
    @{Name = 'SystemComponent'; expression = {[int]$_.SystemComponent}},
    @{Name = 'NoModify'; expression = {[string]$_.NoModify}},
    @{Name = 'NoRepair'; expression = {[string]$_.NoRepair}},
    @{Name = 'ModifyPath'; expression = {[string]$_.ModifyPath}},
    @{Name = 'BundleVersion'; expression = {[string]$_.BundleVersion}},
    @{Name = 'EngineVersion'; expression = {[string]$_.EngineVersion}}

Solution

  • I would only cast the properties that need to be of type int. Since PowerShell is a language based on dynamic typing, so you can do the following:

    $obj = [PSCustomObject] @{ Number = "123" }
    $obj.Number.GetType() # Type is string
    $obj.Number = [int] $obj.Number
    $obj.Number.GetType() # Type is int
    
    Output:
    IsPublic IsSerial Name                                     BaseType
    -------- -------- ----                                     --------
    True     True     String                                   System.Object
    True     True     Int32                                    System.ValueType
    

    You can find this sample online. So, you should be able to use this approach:

    $Data.Language =  [int] $Data.Language
    

    In short, you've cast the properties that need to be of type int.

    UPDATE 1

    If your object have a "flat" hirarchy you can try the following:

    $obj = [PSCustomObject]@{
        IntNr = "123"
        DecNr = "4,56"
        Str   = "abc"
    }
    
    $result = $obj.PSObject.Properties | ForEach-Object {
        [int] $parsedInt = 0
        [decimal] $parsedDec = 0.0
        if ([int]::TryParse($_.Value, [ref]$parsedInt)) {
            $_.Value = $parsedInt
        }
        elseif ([decimal]::TryParse($_.Value, [ref]$parsedDec)) {
            $_.Value = $parsedDec
        }
        $_
    }
    
    $result
    

    Output when dumping $result:

     Value           : 123
     MemberType      : NoteProperty
     IsSettable      : True
     IsGettable      : True
     TypeNameOfValue : System.Int32
     Name            : IntNr
     IsInstance      : True
    
     Value           : 456
     MemberType      : NoteProperty
     IsSettable      : True
     IsGettable      : True
     TypeNameOfValue : System.Decimal
     Name            : DecNr
     IsInstance      : True
    
     Value           : abc
     MemberType      : NoteProperty
     IsSettable      : True
     IsGettable      : True
     TypeNameOfValue : System.String
     Name            : Str
     IsInstance      : True
    

    The sample is available online under this link.