Search code examples
powershellserializationdeserializationxml-deserializationpowershell-5.0

Deserialized Object type issues - specifically with Powershell 5 classes and Import-CliXml


I'm having issues with Powershell 5 classes and object types when reimporting deserialized objects using the Import-CliXml command.

I have an object of type Computer and I wish to store this as xml and then reimport this next time the script is run

class Computer 
{
    $Private:hostname
    $Private:ipAddress

    Computer([String] $hostname, [String] $ipAddress)
    {
        $this.hostname = $hostname
        $this.ipAddress = $ipAddress
    }
    static [Computer] reserialize([PSObject] $deserializedComputer)
    {
        return [Computer]::new($deserializedComputer.hostname, $deserializedComputer.ipAddress)
    }
}

I export and import the object using the following:

$computer = [Computer]::new("test-machine", "192.168.1.2")
$computer | Export-CliXml C:\Powershell\exportTest.xml
$deserializedComputer = Import-CliXml C:\Powershell\exportTest.xml

I understand that when this object is reimported that it is deserialized and is basically just a data container (of type [Deserialized.Computer]). I'm trying to figure out how to type check this object before I try and reserialize it using my reserialize method.

For example if I try and cast $deserializedComputer it tells me that:

Cannot convert value "Computer" to type "Computer". Error: "Cannot convert the "Computer" value of type "Deserialized.Computer" to type 
"Computer"."

I understand why this can't be casted, I'm just using the error message to point out that the object has knowledge that it's of type [Deserialized.Computer]

I can find nothing returned from $deserializedComputer.getMember() that indicates that it is of type [Deserialized.Computer], the only information I can find is that it's of type [PSObject], how can I type check that this object is indeed of type [Deserialized.Computer]?

I should add that type [Deserialized.Computer] doesn't exist at run-time so I can't use this directly in my code, otherwise I would simply use:

$deserializedComputer.getType() -eq [Deserialized.Computer]

Solution

  • Use (Hint: gm is the alias for Get-Member)

    $type = $deserializedComputer | gm | Select -Property TypeName -First 1
    

    then you should be able to access the value like

    $type.TypeName
    

    You can also type check to ensure that it is a Computer by using

    $deserializedComputer.ToString()
    

    Or if you want another way use

    [type]$deserializedComputer.ToString()
    

    Edit:

    You can check it by using the following

    # you can also use $deserializedComputer.ToString() -eq [Computer].ToString()
    if ([type]$deserializedComputer.ToString() -eq [Computer])
    {
    
    }
    

    Your Full class would look something like:

    class Computer 
    {
        $Private:hostname
        $Private:ipAddress
    
        Computer(){}
    
        Computer([String] $hostname, [String] $ipAddress)
        {
            $this.hostname = $hostname
            $this.ipAddress = $ipAddress
        }
        static [Computer] reserialize([PSObject] $deserializedComputer)
        {
        # you can also use $deserializedComputer.ToString() -eq [Computer].ToString()
        if ([type]$deserializedComputer.ToString() -eq [Computer])
        {
           return [Computer]::new($deserializedComputer.hostname, $deserializedComputer.ipAddress)
        }
        return [Computer]::new()
        }
    }
    

    And Exporting/Importing

    $filePath = "C:\Powershell\exportTest.xml"
    
    $computer = [Computer]::new("test-machine", "192.168.1.2")
    $computer | Export-CliXml $filePath
    $deserializedComputer = Import-CliXml $filePath
    

    And Reserialize method

    [Computer]::reserialize($deserializedComputer)