2

I am using PowerShell to call the GitHub API. The result is a JSON array which I convert to a PowerShell object using ConvertFrom-Json cmdlet. This gives me a PowerShell array of objects. However when I pipe this directly to Select-Object I get nothing:

Invoke-WebRequest -Uri "https://api.github.com/organizations?per_page=3" | ConvertFrom-Json | Select-Object -Property login, id

However if I put the ConvertFrom-Json result into a variable, and then pass the variable to Select-Object, it works:

$json = Invoke-WebRequest -Uri "https://api.github.com/organizations?per_page=3" | ConvertFrom-Json 
$json | Select-Object -Property login, id

I am mystified. Why does the one-line version not work?

Edward
  • 8,028
  • 2
  • 36
  • 43
  • 1
    I'm not sure why that is occurring, but if you use invoke-restmethod you won't need to use convertfrom-json as it will auto convert it to an object. – Mark Wragg Mar 16 '17 at 19:06
  • I didn't know about Invoke-RestMethod - thank you - that is nice. I still need the temporary variable though. The mystery remains. – Edward Mar 16 '17 at 19:31
  • Example 2 on here suggests your code should be working https://msdn.microsoft.com/en-us/powershell/reference/5.1/microsoft.powershell.utility/convertfrom-json – Mark Wragg Mar 16 '17 at 19:42
  • Funnily enough I was on that page earlier today, which is why I am mystified: it looks like it should work. And it does work when it's on its own. But when you pipe it, it seems you need the parens or temporary variable. – Edward Mar 16 '17 at 19:49

3 Answers3

1

I'm not sure why the one liner doesn't work in its current form, but this might fix it:

(Invoke-WebRequest -Uri "https://api.github.com/organizations?per_page=3").content | ConvertFrom-Json | Select-Object -Property login, id

Or you can do this instead:

(Invoke-RestMethod -Uri "https://api.github.com/organizations?per_page=3") | Select-Object -Property login, id
techvice
  • 1,315
  • 1
  • 12
  • 24
Mark Wragg
  • 22,105
  • 7
  • 39
  • 68
  • Excellent - Your first example works (I don't need .content but it does no harm either). Your second example does not work as-is, I need to add the parentheses. So my question now is: why do I need the temporary variable OR parentheses to make it work? – Edward Mar 16 '17 at 19:28
  • It's maybe something to do with how Invoke-WebRequest starts to send things in to the pipeline, my guess is that the brackets ensure the command completes entirely before it goes down the pipeline. But there's probably someone more knowledgable about the internals of powershell who might be able to answer. – Mark Wragg Mar 16 '17 at 19:37
1

This happens because that's how powershell works.

Invoke-WebRequest -Uri "https://api.github.com/organizations?per_page=3" | ConvertFrom-Json | Get-Member

Returns an array, so if you start doing select-object on array, it won't work, because array doesn't have those properties, you can work around that, by piping to foreach:

Invoke-WebRequest -Uri "https://api.github.com/organizations?per_page=3" | ConvertFrom-Json | Foreach-Object {  $_ | select id,login }

edit: Invoke-RestMethod is the better way of doing that. edit2: parentheses around the command make that command execute and send whole output when the command is done, so piping starts only after the command was completed, but when the first result is ready.

4c74356b41
  • 69,186
  • 6
  • 100
  • 141
0

The reason seems to be that Invoke-WebRequest and Invoke-RestMethod put their output on the pipeline as a single item, even if that output is an array. Storing the output in a variable and then piping it in, or running the web request in a subexpression causes the array elements to be put in the pipeline instead of the array itself.

> Invoke-RestMethod -Uri "https://api.github.com/organizations?per_page=3" | % { $_.GetType() }

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


> (Invoke-RestMethod -Uri "https://api.github.com/organizations?per_page=3") | % { $_.GetType() }

IsPublic IsSerial Name                                     BaseType
-------- -------- ----                                     --------
True     False    PSCustomObject                           System.Object
True     False    PSCustomObject                           System.Object
True     False    PSCustomObject                           System.Object
Dave
  • 4,420
  • 1
  • 34
  • 39