Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
924 views
in Technique[技术] by (71.8m points)

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

I'm having trouble with adding hashtables to a multidimensional array. I coded the following:

$Data = @{BIBs = @(
    @{$BIB = @{BIBName=$BIBName}, 
    @{Standort = $Standort}, 
    @{Bücher = @(
        @{BuchName = $BuchName; 
        Autor = $Autor
        })
    }}
)}

This code is functioning and creates an output, which I store in a JSON:

{
    "BIBs": [
        {
            "BIB1": [
                {
                    "BIBName": "123"
                },
                {
                    "Standort": "123"
                },
                {
                    "Bücher": [
                        {
                            "Autor": "123",
                            "BuchName": "123"
                        }
                    ]
                }
            ]
        },
        {
            "BIB2": [
                {
                    "BIBname": "345"
                },
                {
                    "Standort": "345"
                },
                {
                    "Bücher": [
                        {
                            "Autor": "345",
                            "Buchname": "345"
                        }
                    ]
                }
            ]
        }
    ]
}

I have extra code which adds another hashtable to array "BIBs" as you can see.

$jsonfile = "C:SkripteibV2-1000.json"
$Data = Get-Content $jsonfile | ConvertFrom-Json
$Data.BIBs += New-Object -TypeName PSObject -Property @{
    $BIB = @{BIBname=$BIBName}, @{Standort=$Standort},
           @{Bücher = @(@{Buchname=$BuchName;Autor=$Autor})}
}

When the output is like above, I'm not able to add another hashtable to "Bücher". I checked the type of "Bücher" with

$data.BIBs.BIB1.Bücher.GetType()

and it's actually an array:

IsPublic IsSerial Name     BaseType
-------- -------- ----     --------
True     True     Object[] System.Array

I tried

$Data.BIBs.BIB1.Bücher += @{Person="Max";Alter="35"}

to add a new hashtable, like I did with "BIB2", but I am getting the error:

The property 'Bücher' cannot be found on this object. Verify that the property
exists and can be set.
At line:5 char:1
+ $data.BIBs.BIB1.Bücher += @{Motor="asdf";pers="345"}
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidOperation: (:) [], RuntimeException
    + FullyQualifiedErrorId : PropertyAssignmentException

Do you know how I add @{Person="Max";Alter="35"} to "Bücher"?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

tl;dr

Setting a key / property value via member 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 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 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 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 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 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.
...

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...