Title: Objects are situationally invisibly [psobject]-wrapped, sometimes causing unexpected behavior. · Issue #5579 · PowerShell/PowerShell · GitHub
Open Graph Title: Objects are situationally invisibly [psobject]-wrapped, sometimes causing unexpected behavior. · Issue #5579 · PowerShell/PowerShell
X Title: Objects are situationally invisibly [psobject]-wrapped, sometimes causing unexpected behavior. · Issue #5579 · PowerShell/PowerShell
Description: There are (at least?) 5 basic scenarios in which objects / properties end up invisibly [psobject]-wrapped, which can lead to subtle differences in behavior: Note: By [psobject]-wrapped I mean an object for which -is [psobject] returns $t...
Open Graph Description: There are (at least?) 5 basic scenarios in which objects / properties end up invisibly [psobject]-wrapped, which can lead to subtle differences in behavior: Note: By [psobject]-wrapped I mean an ob...
X Description: There are (at least?) 5 basic scenarios in which objects / properties end up invisibly [psobject]-wrapped, which can lead to subtle differences in behavior: Note: By [psobject]-wrapped I mean an ob...
Opengraph URL: https://github.com/PowerShell/PowerShell/issues/5579
X: @github
Domain: github.com
{"@context":"https://schema.org","@type":"DiscussionForumPosting","headline":"Objects are situationally invisibly [psobject]-wrapped, sometimes causing unexpected behavior.","articleBody":"There are (at least?) 5 basic scenarios in which objects / properties end up invisibly `[psobject]`-wrapped, which can lead to subtle differences in behavior:\n\nNote: By `[psobject]`-wrapped I mean an object for which `-is [psobject]` returns `$true`.\n\n* Objects _received via the pipeline_ when processed via `$_` / `$PSItem` or an `[object]` or _untyped_ parameter - but _not_ if passed as an _argument_:\n\n```powershell\n### Via the *pipeline*:\n# -\u003e $true: use of $_\n'foo' | ForEach-Object { $_ -is [psobject] }\n# -\u003e $true: use of [object]-typed parameter (or untyped)\n'foo' | \u0026 { param([parameter(valuefrompipeline)] [object] $foo) process { $foo -is [psobject] } }\n\n# -\u003e $false: use of a specifically typed parameter\n'foo' | \u0026 { param([parameter(valuefrompipeline)] [string] $foo) process { $foo -is [psobject] } }\n\n### As an *argument*:\n# -\u003e $false: untyped parameter (or [object]-typed), or any type other than [psobject]\n\u0026 { param($p) $p -is [psobject] } 'foo'\n\u0026 { param([string] $p) $p -is [psobject] } 'foo'\n# -\u003e $true: only with explicit [psobject]-typing:\n\u0026 { param([psobject] $p) $p -is [psobject] } 'foo'\n```\n \n* Objects _output by a - binary - cmdlet_ - but *not* objects output by a *PowerShell* function or script or objects returned from an _expression_. (Note that the arrays that PowerShell implicitly constructs for collecting _multiple_ output objects on assignment to a variable or when a command call participates in an expression are themselves _not_ `[psobject]`-wrapped.)\n\n```powershell\n# -\u003e $true: output from binary *cmdlet*\n(Write-Output 'foo') -is [psobject]\n\n# -\u003e $false: output from *PowerShell code*\n(\u0026 { 'foo' }) -is [psobject]\n\n# -\u003e $false: output from an expression\n'foo' -is [psobject]\n```\n\n* The value of any _calculated property_ whose value is determined via a _script block_ - but _not_ if the value is determined by a property _name_ (string):\n\n```powershell\n# -\u003e $true: property value is determined by *script block*\n(Get-Item / | Select-Object @{ l='foo'; e={ $_.Name } }).foo -is [psobject]\n\n# -\u003e $false: property value is determined by *name* (string)\n(Get-Item / | Select-Object @{ l='foo'; e='Name' }).foo -is [psobject]\n```\n\n* The elements of the collections returned by the intrinsic `.ForEach()` and `.Where()` methods are always `[psobject]`-wrapped (implied by the output collection type, `System.Collections.ObjectModel.Collection\u003cPSObject\u003e`):\n\n```powershell\n# -\u003e $true, $true\n(1..2).ForEach({ $_ }) | ForEach-Object { $_ -is [psobject] }\n```\n\n* Perhaps not surprisingly, if you cast / type-constrain with `[psobject]` - for which there is no need, and in *most* scenarios this will have *no* practical implications; more surprisingly, however, the same applies to `[pscustomobject]`, given that these type accelerators are effectively _the same_ and _both_ refer to `[System.Management.Automation.PSObject]` (see #4344).\n\n```powershell\n# -\u003e $true\n([psobject] 42) -is [psobject]\n\n# -\u003e $true: [pscustomobject] is the same as [psobject] and \n# does *not* create a custom object from arbitrary operands\n# (only [pscustomobject] @{ ... } works)\n([pscustomobject] 42) -is [psobject]\n```\n\n---\n\nAs for **real-world ramifications** (in addition to the difference in `Get-Member` representation - see below):\n\n* In *Windows PowerShell*, `ConvertTo-Json` is affected, as originally reported \nin #3153 - see below.\n(*PowerShell Core* is no longer affected, because that issue has been fixed\nvia #3231)\n\n* #5763\n\n* #14355\n\n* #17199 [declared to be by design]\n\n* #14829\n\n* #10687 [fixed in v7.0.0-preview.5]\n\n* https://github.com/PowerShell/PowerShell/issues/20401#issuecomment-1741082589\n\n* #20450\n\n* #21260\n\n* #21496\n\n* #24554\n\n* #14394\n\n* #24671\n\n* #24740\n\n* https://github.com/PowerShell/PowerShell/issues/19185#issuecomment-1442400683\n\n* #27558\n\n* `$_` in a `ForEach-Object` script block reflects each input object `[psobject]`-wrapped, causing `-is [pscustomobject]` tests to become meaningless, as then _any_ input object passes the test - see [this SO question](https://stackoverflow.com/q/53268394/45375)\n\n* Passing an incidentally wrapped `[psobject]`-wrapped instance to an external serialization API such as `[Newtonsoft.Json.JsonConvert]::SerializeObject()` causes unexpected behavior - see [this Stack Overflow post](https://stackoverflow.com/a/63588539/45375).\n\n* For the sake of completeness: bugs that still affect _Windows PowerShell_, but have since been fixed: \n \n * An `[xml]`-related bug: see [this Stack Overflow post](https://stackoverflow.com/q/79673464/45375).\n \n * The `$Transcript` preference variable doesn't recognize a `[psobject]`-wrapped string (such as output by `Join-Path`) as a string: see [this Stack Overflow post](https://stackoverflow.com/q/79690475/45375).\n\n\nNote that even non-wrapped instances in the sense above _do_ have ETS-defined properties (e.g., `[datetime]::now.psextended` reveals the `.DateTime` property).\n\n(While #4347 may sound related, the distinct issue there is that _additional_ properties may be added to outputs by _provider_ cmdlets.)\n\nExamples:\n\n```powershell\n# Two seemingly equivalent ways of constructing a [string] instance:\n# Using an expression vs. using a command:\n$o1 = 'hi'; $o2 = New-Object System.String 'hi'\n\n# Only the New-Object (*command*-generated) instance is wrapped, however.\n\u003e ($o1 -is [psobject]), ($o2 -is [psobject])\nFalse\nTrue\n\n# With *multiple outputs*, *only the individual elements are wrapped*,\n# because the implicitly constructed array that collects the output is itself\n# NOT wrapped.\n\u003e $arr = Write-Output 1, 2; $arr -is [psobject]; $arr[0] -is [psobject]\nFalse\nTrue\n\n# Two ways of constructing a [pscustomobject] instance\n# with an array-valued .foo property that is *not* wrapped\n# ($o2 itself, by contrast, *is* wrapped, because it is constructed with\n# a *conmand*).\n$o1 = [pscustomobject] @{ foo = 1, 2 }\n$o2 = New-Object pscustomobject -property @{ 'foo' = 1, 2 }\n\n# *Seemingly* equivalent ways, which, however result in a [psobject]-wrapped\n# .foo property.\n# Calculated property:\n$o3 = '' | Select-Object @{ l='foo'; e = { 1, 2 } }\n# Explicit [psobject] cast:\n$o4 = [pscustomobject] @{ foo = [psobject] (1, 2) }\n\n# All are instances of [System.Management.Automation.PSCustomObject]\n\u003e $o1, $o2, $o3, $o4 | % GetType | % Name\nPSCustomObject\nPSCustomObject\nPSCustomObject\nPSCustomObject\n\n# All .foo properties are instances of [System.Object[]]\n\u003e $o1.foo, $o2.foo, $o3.foo, $o4.foo | % GetType | % Name\nObject[]\nObject[]\nObject[]\nObject[]\n\n# However, only $o3 and $o4's .foo properties are considered [psobject] instances:\n\u003e ($o1.foo -is [psobject]), ($o2.foo -is [psobject]), ($o3.foo -is [psobject]), ($o4.foo -is [psobject])\nFalse\nFalse\nTrue\nTrue\n\n# This subtle distinction can result in different behavior.\n# Note how Get-Member represents the properties differently.\n\u003e $o1, $o3 | Get-Member foo | % Definition\nObject[] foo=System.Object[]\nSystem.Object[] foo=1 2\n\n# *Windows PowerShell* only:\n# Note how the JSONification of $o3 has an extraneous \"value\" wrapper for\n# the array and a \"Count\" property.\n\u003e $o1, $o3 | ConvertTo-Json\n[\n {\n \"foo\": [\n 1,\n 2\n ]\n },\n {\n \"foo\": {\n \"value\": [\n 1,\n 2\n ],\n \"Count\": 2\n }\n }\n]\n```\n\n\nEnvironment data\n----------------\n\nCurrent as of:\n\n```powershell\nPowerShell Core 7.5.0-rc.1\nWindows PowerShell 5.1\n```\n","author":{"url":"https://github.com/mklement0","@type":"Person","name":"mklement0"},"datePublished":"2017-11-29T18:28:21.000Z","interactionStatistic":{"@type":"InteractionCounter","interactionType":"https://schema.org/CommentAction","userInteractionCount":3},"url":"https://github.com/5579/PowerShell/issues/5579"}
| route-pattern | /_view_fragments/issues/show/:user_id/:repository/:id/issue_layout(.:format) |
| route-controller | voltron_issues_fragments |
| route-action | issue_layout |
| fetch-nonce | v2:ec79a154-f9fd-39d8-d7fb-b06864b259ae |
| current-catalog-service-hash | 81bb79d38c15960b92d99bca9288a9108c7a47b18f2423d0f6438c5b7bcd2114 |
| request-id | 813A:13DCF0:11202A0:1653DCF:6A5B0122 |
| html-safe-nonce | 4d919464dfed98b6d33368fb67e9d8fed129b99a451210f299064b2baaf04f66 |
| visitor-payload | eyJyZWZlcnJlciI6IiIsInJlcXVlc3RfaWQiOiI4MTNBOjEzRENGMDoxMTIwMkEwOjE2NTNEQ0Y6NkE1QjAxMjIiLCJ2aXNpdG9yX2lkIjoiMzQ3NzE0MzE2NTk5NTc3ODMzOCIsInJlZ2lvbl9lZGdlIjoiaWFkIiwicmVnaW9uX3JlbmRlciI6ImlhZCJ9 |
| visitor-hmac | 6958bb3d8b3aae6989e4677e82a617bda59826153d57db2bb77d1b436d72b435 |
| hovercard-subject-tag | issue:277859485 |
| github-keyboard-shortcuts | repository,issues,copilot |
| google-site-verification | Apib7-x98H0j5cPqHWwSMm6dNU4GmODRoqxLiDzdx9I |
| octolytics-url | https://collector.github.com/github/collect |
| analytics-location | / |
| fb:app_id | 1401488693436528 |
| apple-itunes-app | app-id=1477376905, app-argument=https://github.com/_view_fragments/issues/show/PowerShell/PowerShell/5579/issue_layout |
| twitter:image | https://opengraph.githubassets.com/9ec9a599a508c94ec7652ff4baa2cb8721fb9bd5f98b34d117e4fa74cf13792e/PowerShell/PowerShell/issues/5579 |
| twitter:card | summary_large_image |
| og:image | https://opengraph.githubassets.com/9ec9a599a508c94ec7652ff4baa2cb8721fb9bd5f98b34d117e4fa74cf13792e/PowerShell/PowerShell/issues/5579 |
| og:image:alt | There are (at least?) 5 basic scenarios in which objects / properties end up invisibly [psobject]-wrapped, which can lead to subtle differences in behavior: Note: By [psobject]-wrapped I mean an ob... |
| og:image:width | 1200 |
| og:image:height | 600 |
| og:site_name | GitHub |
| og:type | object |
| og:author:username | mklement0 |
| hostname | github.com |
| expected-hostname | github.com |
| None | 5290d7e14309ad1e76106a9c4237bd1041517e83ea182c8ab756752cb0c6940b |
| turbo-cache-control | no-preview |
| go-import | github.com/PowerShell/PowerShell git https://github.com/PowerShell/PowerShell.git |
| octolytics-dimension-user_id | 11524380 |
| octolytics-dimension-user_login | PowerShell |
| octolytics-dimension-repository_id | 49609581 |
| octolytics-dimension-repository_nwo | PowerShell/PowerShell |
| octolytics-dimension-repository_public | true |
| octolytics-dimension-repository_is_fork | false |
| octolytics-dimension-repository_network_root_id | 49609581 |
| octolytics-dimension-repository_network_root_nwo | PowerShell/PowerShell |
| turbo-body-classes | logged-out env-production page-responsive |
| disable-turbo | false |
| browser-stats-url | https://api.github.com/_private/browser/stats |
| browser-errors-url | https://api.github.com/_private/browser/errors |
| release | 9c975978430e9ad293956f2bbdaf153b1bd84a99 |
| ui-target | full |
| theme-color | #1e2327 |
| color-scheme | light dark |
Links:
Viewport: width=device-width