Powershell: Array de/serialization and type checking

Created on 22 Nov 2019  路  4Comments  路  Source: PowerShell/PowerShell

Steps to reproduce

I have a PSCustomObject $bigObject coming imported from this JSON:

{
  "psco": {
    "somekey1": "somevalue",
    "somekey2": "somevalue"
  },
  "array": [
    "someitem1",
    "someitem2"
  ]
}

psco.getType() --> PSCustomObject
array.getType() --> Object[]

as expected.
Now, if I serialize and deserialize the whole object, with [System.Management.Automation.PSSerializer], the resulting object type change to:

psco.getType() --> PSCustomObject
array.getType() --> System.Collection.ArrayList

  • Is this type change to be expected or is it a bug?

Because this ($bigObject.array -is [PSCustomObject])
returns false before serialization (good) and false (bad) after deserialization.

  • Is it also to be expected that the ArrayList is considered equal to a PSCustomObject"?*

Thank you!

Environment data

Name                           Value
----                           -----
PSVersion                      6.2.3
PSEdition                      Core
GitCommitId                    6.2.3
OS                             Microsoft Windows 10.0.18363
Platform                       Win32NT
PSCompatibleVersions           {1.0, 2.0, 3.0, 4.0鈥
PSRemotingProtocolVersion      2.3
SerializationVersion           1.1.0.1
WSManStackVersion              3.0
Issue-Question WG-Engine

All 4 comments

As a non-PS team member that seems normal to me, you can try using the -AsHashTable parameter since your on PSCore if you want something more consistent.

It definitely seems odd to me that an array is being deserialized to a much more complex and heavy type instead of just being emitted as an array.

The JSON serialization seems correct... the PSSerializer is what's used in the CliXML cmdlets, isn't it? That should definitely be maintaining the original type.

Is this type change to be expected or is it a bug?

The serialization infrastructure preserves type fidelity only for a relatively small number of known types (but always records the full, original type name and that of the types in its inheritance hierarchy).

List-like data structures (collections) are serialized as abstract <LST> elements, as described in Contents of Known Containers in the MS-PSRP spec.

On deserializing, the various collection types serialized as <LST> are seemingly _all_ deserialized as [System.Collections.ArrayList] instances.

While there are definitely many cases where type-faithful deserialization _isn't_ possible, you definitely _could_ faithfully "rehydrate" instances of type [object[]] (and, obviously, [System.Collections.ArrayList] itself, as well as - depending on the specific element type - [System.Collections.Generic.List[object]]).

It is unclear to me why the deserialize-all-lists-to-ArrayList approach was chosen (other than for implementation convenience), though, pragmatically speaking, it probably won't be a problem in practice too often.

Is it also to be expected that the ArrayList is considered equal to a PSCustomObject

The short of it is that you shouldn't test for custom objects with -is [pscustomobject], because it is effectively the same as -is [psobject] (see #4344) and that can _situationally_ return $true for _any_ data type instance that happens to be wrapped in an extra, otherwise invisible [psobject] instance, as in your case - see #5579.

Instead, obscurely, you must use -is [System.Management.Automation.PSCustomObject], which only returns true for true custom objects.

The caveat is that the -as variant does _not_ work - see #4343.

@mklement0 that is great info! Thank you!

Was this page helpful?
0 / 5 - 0 ratings

Related issues

vexx32 picture vexx32  路  70Comments

joeyaiello picture joeyaiello  路  99Comments

DarwinJS picture DarwinJS  路  65Comments

andschwa picture andschwa  路  64Comments

dragonwolf83 picture dragonwolf83  路  127Comments