0

There is a custom type, which is defined like:

class User
{
    public int Age { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
}

I have to convert the PSCustomObject which has the following properties to the User type.

@{
    Age:12;
    FirstName:'John';
    LastName:'Snow';
    Address:'Whatev';
    Sex:'Male'
}

But whenever I try to cast the ps object to the custom type like this: $john = [User]$psObj I get InvalidCastConstructorException. Obviously it appears because the PsCustomObject has many more properties then the User type supports.

The question is: do we have an easy way to convert such PSCustomObject data to custom type?

P.S. I have to support powershell 5.1

P.S.S I've seen similar issue, but I have a slightly different case. I'm not limited to the explicit conversion in the function parameters list

UPDATE I came up with the next function

function Convert-PSCustomObjectToCustomType {
    param (
        [System.Reflection.TypeInfo]$customType,
        [PSCustomObject]$psCustomObj
    )

    $validObjProperties = @{}
    $customObjProperties = $customType.GetProperties() | ForEach-Object { $_.Name }
    foreach ($prop in $psCustomObj.PSObject.Properties) {
        if ($customObjProperties -contains $prop.Name) {
            $validObjProperties.Add($prop.Name, $prop.Value)
        }
    }

    return New-Object -TypeName $customType.FullName -Property $validObjProperties
}
managerger
  • 728
  • 1
  • 10
  • 31

2 Answers2

2

You can use a hashtable literal as an initializer when casting to the target type:

# Assuming your object looks like this ...
$object = [pscustomobject]@{
    Age       = 12
    FirstName = 'John'
    LastName  = 'Snow'
    Address   = 'Whatev'
    Sex       = 'Male'
}

# ... you can initialize the properties of a new instance by casting a dictionary like this
$target = [User]@{
  Age       = $object.Age
  FirstName = $object.FirstName
  LastName  = $object.LastName
}
Mathias R. Jessen
  • 157,619
  • 12
  • 148
  • 206
  • Yes, that's a valid option and it does solve my current issue. But I need something more extensible. Object could change in the future. I don't want to update it across all the codebase. Thanks, sir! – managerger Aug 19 '20 at 15:30
1

For my own benefit I did exactly what was in the linked answer and it seems to work with simple casting:

Run code like below should give you the "User" type

class User  {
    [Int]$Age
    [String]$FirstName
    [String]$LastName
    
    # Cast-able constructor (I think...) :
    User([Object]$PSObject){
        $this.Age = $Object.Age
        $this.FirstName = $Object.FirstName
        $this.LastName = $Object.LastName
    }        
}

$object = [pscustomobject]@{
        Age = 12
        FirstName = 'John'
        LastName = 'Snow'
        Address = 'Whatev'
        Sex = 'Male'
    }

$NewObject = [User]$Object

$NewObject.GetType()

I don't play with classes very often, but I'm inclined to think you should have a more generic constructor as well:

User(
    [Int]$a,
    [string]$f,
    [string]$l
){
    $this.Age = $a
    $this.FirstName = $f
    $this.LastName = $l
}

This would have Overload defs like:

OverloadDefinitions
-------------------
User new(System.Object Object)
User new(int a, string f, string l)

I modeled that second constructor directly off MS documentation.

Note: As with functions I often get confused about typing arguments when the incoming object type might be unknown. I've used [PSObject] here only because everything is descended from [Object] and I didn't want to narrow the input to PSCustomObject specifically. Again I don't work with classes much so I'm open to and interested in corrections.

halfer
  • 19,824
  • 17
  • 99
  • 186
Steven
  • 6,817
  • 1
  • 14
  • 14
  • A class defined in PowerShell does not behave the same way a normal .NET class does, which the question seems to imply is being used here (as it uses C# syntax), though that would be worth clarifying. – Jeroen Mostert Aug 19 '20 at 16:55
  • Keep in mind this approach presumes OP has complete control over (and the opportunity to modify) the target type. – Mathias R. Jessen Aug 19 '20 at 17:00