0

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}}
Moerwald
  • 10,448
  • 9
  • 43
  • 83
Arbiter
  • 450
  • 5
  • 26

1 Answers1

1

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.

Moerwald
  • 10,448
  • 9
  • 43
  • 83
  • Hi Moerwald, thanks for that information. I limited my question to integer to see if there was some obvious method that I was missing, unfortunately I'm dealing with 30+ objects and 400+ properties which include all of the different data types. From the looks of it, it seems that the options are either cast using an expression or loop through each object and cast using your example above? – Arbiter Jun 24 '19 at 17:49
  • @Arbiter, see update of my answer. Yes, I think you've to walk through the properties and find the correct type to cast to. – Moerwald Jun 25 '19 at 05:08
  • That's a great answer and has given me something to think about. Thank you for your help! – Arbiter Jun 25 '19 at 12:04