How to add hashtable to multidimensional array? Cannot assign values via member enumeration

Solution 1:

tl;dr

Setting a key / property value via member-access enumeration is not supported (see below).

Instead, you must obtain the specific object whose .Bücher property you want to modify explicitly:

($Data.BIBs.BIB1 | ? Bücher).Bücher += @{ BuchName='neues Buch'; Autor='Johann Doe' }

Note: This assumes that:

  • only one element of array $Data.BIBs.BIB1 has a .Bücher property (key)
  • that, if the property / key does exist, it is nonempty and therefore is "truthy" in a Boolean context, such as the expression passed to ? (Where-Object); like member-access enumeration, this simplified Where-Object syntax - ? Bücher instead of ? { $_.Bücher } - is a PSv3+ feature called comparison statement.

Mathias R. Jessen has provided the crucial pointer in comments on the question:

PowerShell has an intentional asymmetry with respect to dot notation across collection-valued properties for getting values vs. setting values.

  • On getting, PSv3+ applies member-access enumeration, which, in a nutshell, allows you to access a property on a collection and implicitly get that property's value from each element IN that collection, with the results getting collected in an array.

  • On setting, member-access enumeration is not applied; the rationale is that the risk of unintentional modification of data is too high - see GitHub issue #5271 and in particular this comment by a core member of the PS team.

The unfortunate aspect is that the current error message doesn't tell you that.
It stems from the fact that when attempting to set a property at the collection level, the property is looked for only directly on the collection (instead of on its elements), where it (usually) doesn't exist.

Let's take a look at a simplified example:

$data = @{ # a hashtable
  a = ( # array of hashtables
    @{ b1 = 'b1' },
    @{ b2 = 'b2' },
    @{ b3 = 
      @{ b31 = 'b31' }, @{ b32 = 'b32' } # array of hashtables
    }
  )
}

On getting, everything works fine:

PS> $data.a.b3

Name                           Value                                                                                                                                                                                                                            
----                           -----                                                                                                                                                                                                                            
b31                            b31                                                                                                                                                                                                                              
b32                            b32                                                                                                                                                                                                                              

Even though $data.a is an [object[]] array, an object (hashtable) with property .b3 was found among its elements, and that object's .b3 value is output.
This is member-access enumeration in action (although the more typical uses is case for the property to exist on all elements of the array and for the individual values to be collected in an [object[]] array).

On setting, PowerShell forgoes member-access enumeration and therefore unsuccessfully looks for a .b3 property only directly on the [object[]] instance that is $data.a and, of course, arrays have no .b3 property:

PS> $data.a.b3 += @{ b33 = 'b33' }  # Try to add an element; !! FAILS

The property 'b3' cannot be found on this object. 
Verify that the property exists and can be set.
...