简体   繁体   中英

Looping through API XML data is not being added to the array correctly

Here is the XML. I am looking for the siteid.

<result created="2018-10-13T13:11:18-05:00" host="redacted" status="OK">
<items>
<site>
<siteid>399700</siteid>
<name>
<![CDATA[ Warehouse ]]>
</name>
<connection_ok>1</connection_ok>
</site>
<site>
<siteid>547401</siteid>
<name>
<![CDATA[ Monterey Park ]]>
</name>
<connection_ok>1</connection_ok>
</site>
</items>
</result>

To obtain the data I am looking for, I first need to iterate through the XML for a client id, and then I can use the client id to find the site id for each client. Some clients have multiple sites.

While the clients are obtained correctly the sites are, for the lack of a better term, compressed.

Results:

ClientID: 12345
SiteID: 9876543210

The Site ID should be two individual numbers: 98765 43210

Here are the two functions I am working with:

$ClientsIDs = @()
$SiteIDs = @()

function Get-Clients() {
    $clientdata = Invoke-RestMethod -Uri ($baseurl + $lc)
    $clientid = $clientdata.result.items.client.clientid
    $Script:ClientsIDs += $clientid
    Write-Output("Client ID: " + $ClientsIDs)
}

function Get-Sites() {
    foreach ($id in $Global:ClientsIDs) {
        $sitedata = Invoke-RestMethod -Uri ($baseurl + $ls + $id)
        $siteid = $sitedata.result.items.site.siteid 
        $SiteIDs += $siteid
    }
    Write-Output("Site ID: " + $SiteIDs)
}

Get-Clients
Get-Sites

(Only the URLs are not shown. The Write-Output s are only for my benefit to make sure the data is being collected correctly. They will eventually be removed.)

How can I get the $sitedid to properly be saved in the array?

tl;dr

I suggest refactoring your code, which avoids your problem, and is both more efficient and provides better encapsulation:

function Get-Sites() {
    # Note: Also consider passing the parent-scope 
    #       $ClientsIDs, $baseurl, $ls variables as *parameters* instead.
    foreach ($id in $ClientsIDs){
        $sitedata = Invoke-RestMethod -Uri ($baseurl + $ls + $id)
        # Implicitly output each site ID, which by
        # virtue of being inside a foreach loop outputs
        # all of them as an array.
        $sitedata.result.items.site.siteid 
    }
}

# If you wanted to interpret the IDs as *numbers*, you
# could use type [int[]], for instance
[array] $SiteIDs = Get-Sites

As for what you tried :

By assigning to $SiteIDs without scope modifier script ( $script:SiteIDs ), you're mistakenly creating a local $SiteIDs variable inside your Get-Sites function.

Given that scopes see - but cannot directly assign to - variables from parent scopes, the local copy of $SiteIDs that is being created by the assignment inherits the type (and value) of a variable of the same name from a parent scope (see this answer to learn more about scoping in PowerShell).

If $SiteIDs were truly an array in your script scope, your local copy would also create an array - but that still wouldn't modify the original array in the script scope.

The fact that $SiteIDs in your case ended up containing the string concatenation of your site IDs suggests that your actual code either does not create a $SiteIDs variable in the script scope, or it is string -typed there, not an array ( @() ) , because applying += with a string as the RHS to a variable that is [string] -typed or didn't previously exist performs simple string concatenation (appends the RHS directly to the existing value, which defaults to the empty string, if the variable didn't exist).

The immediate fix would be:

  • Be sure that $SiteIDs = @() is truly defined in the script scope, that is, as an array .

  • Change $SiteIDs += $siteid to $script:SiteIDs += $siteid so as to modify the script-scope variable directly, as intended.

That said, it's generally better to avoid referencing variables across scope boundaries - use parameters and local variables for better encapsulation, as demonstrated at the top.

[array]$SiteIDs += $siteid

# or

function Get-Sites() {
    $SiteIDs=@()
    foreach ($id in $Global:ClientsIDs){
        ...
        $SiteIDs += $siteid

# or; for better performance

function Get-Sites() {
    $SiteIDs=New-Object System.Collections.ArrayList
    foreach ($id in $Global:ClientsIDs){
        ...
        $SiteIDs.add($siteid)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM