I am currently struggling to convert a JSON-string into an array of objects and to GENERALLY handle the properties/attributes of each object.
Here is a simple demo, that shows that e.g. the attribute "address" seems to be a bit special:
cls
$json = '[{"id":"1","address":"1"},{"id":"2","address":"2"}]'
$list = $json | ConvertFrom-Json
$list.id # OK
$list.address # gives a weired result - is this a bug?
$list.GetEnumerator().address # that works
This is the output:
1
2
OverloadDefinitions
-------------------
System.Object&, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 Address(int )
1
2
As you can see, I need to add ".GetEnumerator()" to get the correct "address"-values.
Is this the expected? Should I ALWAYS use the ".GetEnumerator()" to be safe?
Since
$list
is an array (collection), using something like$list.id
performs member-access enumeration; that is, the.id
property is automatically accessed on each element, and the resulting values are returned as an array ([object[]]
)[1].However, if the array / collection type itself has a member by that name (a property or method), it takes precedence, which is what happened in your case: .NET arrays have an
.Address
method (that is added by the runtime - see this answer), and that is what preempted accessing the elements'.Address
property.(What you saw was PowerShell's representation of the method's signature (overload), which is what you get when you access a method by name only, without actually calling it by appending (
That PowerShell provides no distinct operator syntax to distinguish between direct member access and member-access enumeration is the subject of [GitHub issue #7445](https://github.com/PowerShell/PowerShell/issues/7445), but the existing behavior is unlikely to change.(...)
); try'foo'.ToUpper
, fo instance.)The most efficient way to work around that problem is to use the PSv4+
.ForEach()
array method, which always targets the collection's elements:Caveat: If you're running Windows PowerShell and
$list
can situationally just result in a singlepscustomobject
rather than an array, you must enclose$list
in@(...)
, the array-subexpression operator, to ensure that the.ForEach()
method is available. This is a[pscustomobject]
-specific bug (given that even single objects should consistently have a.ForEach()
method), which has been fixed in PowerShell (Core) 6+.Note:
Technically, this returns a
[System.Collections.ObjectModel.Collection[PSObject]]
collection, but in most cases you can use it like a regular[object[]]
PowerShell array.Unlike with member-access enumeration, a collection instance is also returned if there's only one element in the input collection (rather than returning that one element's property value as-is, the way member-access enumeration does).
In PSv3- you can use the
ForEach-Object
cmdlet, orSelect-Object -ExpandProperty
:[1] If there's only one element in the collection, its property value is returned as-is, not wrapped in a (single-element) array, which is the same logic that is applied when collecting pipeline output in a variable. That this logic may be unexpected in the context of member-access enumeration, which is an expression context, is being discussed in GitHub issue #6802.