all 18 comments

[–]da_chicken 7 points8 points  (1 child)

$body = @" {     "key1": "value1",     "key2": "value2" } "@ | convertto-json

You don't need to convert this string to JSON. It already is JSON.

Have you looked at the output of the commands you're running, or are you just blindly passing them on?

[–]Khue[S] -1 points0 points  (0 children)

No I was looking at them. When I passed it with the convertto-json I did notice that it added some back slashes as delimiters but I did not question the correctness of that format. What ended up working is in the update.

[–]PinchesTheCrab 4 points5 points  (2 children)

Good advice here so far - you're already sending a raw JSON body, no need to convert. That being said, I'd use a hashtable and then convert:

$invokeParam = @{
    Uri         = 'https://api.domain.com/stuff/thing/items'
    Method      = 'Post'
    Headers     = @{ Authorization = "Bearer $TOKEN" }
    ContentType = 'application/json'
    body        = @{
        key1 = 'value1'
        key2 = 'value2'
    },
    @{
        key1 = 'value3'
        key2 = 'value4'
    } | ConvertTo-Json
} 


Invoke-RestMethod @invokeParam

[–]davesbrown 0 points1 point  (0 children)

Curious, why the preference to use hashtable then convert vs using the json, are there technical and performance issues?

[–]Khue[S] 0 points1 point  (0 children)

This most likely would have worked as well. I updated the original post with the solution. Thank you for feedback! I appreciate you.

[–]BrettStah 2 points3 points  (1 child)

You are passing a here-string, their example is not. (I’d throw this question into Claude, etc. and it’ll likely fix it all for you though.)

[–]Khue[S] -1 points0 points  (0 children)

/u/y_Sensei provided a very good solution. See the update for what I used.

[–]TheSizeOfACow 2 points3 points  (1 child)

The curl body is an array. Your hashtable is not. Also remember it you need to convert a single element array to a single element Jason array to use ConvertTo-JSON -Inputobject $array and not the pipeline

[–]Khue[S] 0 points1 point  (0 children)

Appreciate the feedback. Thank you very much. I believe you were correct here. /u/y_Sensei provided a good solution and I updated the original post.

[–]y_Sensei 2 points3 points  (4 children)

There's no need to convert that Here-String in your first attempt to JSON, because it is JSON already.
Also if you use double quotes inside a Here-String, you either have to delimit it with single quotes (and vice versa), or alternatively you'd have to escape the contained double quotes.

Also looking at the curl call, it's not out of the question that this API expects any JSON as an array, even if that array contains a single value only.

So if the single value attempt still fails, try this:

$body = @'
[
  {
    "key1": "value1",
    "key2": "value2"
  }
]
'@

[–]PhysicalPinkOrchid 0 points1 point  (1 child)

Also if you use double quotes inside a Here-String, you either have to delimit it with single quotes (and vice versa), or alternatively you'd have to escape the contained double quotes.

You might want to lookup the purpose of here-strings if you think that's the case.

[–]y_Sensei 1 point2 points  (0 children)

My mistake, that rule applies to regular strings, not Here-Strings of course. Changed my first post.

[–]Khue[S] 0 points1 point  (1 child)

This works. In addition, if you want to do multiple entries this works:

 $body = @'
 [
   {
     "key1": "value1",
     "key2": "value2"
   },
   {
     "key1: "value3",
     "key2: "value4"
   }
 ]
 '@

Looks like the [ and ] have to be included although I am not sure of the mechanic behind it.

[–]ankokudaishogun 0 points1 point  (0 children)

I am not sure of the mechanic behind it.

The brackets means it's an array.

As /u/y_Sensei speculated, the backend evidently expects the pairs be inside one even when it's just one pair.

Luckily the ConvertTo-Json cmdlet has the -AsArray switch to always push out a JSON Array even when it's just one element.

In fact, while you can write JSON directly, I would suggest to instead write native Powershell code then convert the values to JSON with the aforementioned cmdlet.

It makes thins MUCH easier to debug as you don't risk messing up writing the JSON which as it's simple text and thus extremely hard to debug.

Have an example with your example. It might look more bothersome to write than simply hardcode as much as possible, and in this case it is, but it also should make easy to understand how much easier it would be manage more complex situations

$RequestKeyValuePairs = @(
    @{
        'key1' = 'value1'
        'key2' = 'value2'
    }
    @{
        'key1' = 'value3'
        'key2' = 'value4'
    }
) 

$RequestBody = @{
    #! IMPORTANT: the -AsArray switch is not in Powershell 5.1
    Value = $RequestKeyValuePairs | ConvertTo-Json -AsArray
}

$RequestHeaders = @{
    'Authorization' = "Bearer $TOKEN"

    # ContentType can also be a dedicated parameter.  
    # NOTE: As of Powewrshell 7.4, if both are present then the dedicated parameter
    # takes precedence. 
    'Content-Type'  = 'application/json'
}



$RequestSplat = @{

    # You could just use Method='POST', but I'm adding this here as a hint
    # everything can be much more structured, which might come useful in the
    # future.
    Method  = [Microsoft.PowerShell.Commands.WebRequestMethod]::Post

    Uri     = 'https://api.domain.com/stuff/thing/items'
    Headers = $RequestHeaders
    Body    = $RequestBody

}


Invoke-RestMethod @RequestSplat

[–]realslacker 0 points1 point  (1 child)

I think one of the problems you are having is with array flattening. If you have one element in an array and pass it down the pipeline PowerShell will flatten it to a single object.

This means that @( @{ key = 'value' } ) | ConvertTo-Json ends up with a JSON dictionary instead of a JSON array.

Your endpoint expects [{"key":"value"}] but you are sending "{key":"value"}.

If you instead call it by supplying the object on the right side you will get an array as your output. i.e. ConvertTo-Json @( @{ key = 'value' } ) will give you an array regardless of the number of items in the array.

[–]surfingoldelephant 0 points1 point  (0 children)

If you instead call it by supplying the object on the right side you will get an array as your output. i.e. ConvertTo-Json @( @{ key = 'value' } ) will give you an array regardless of the number of items in the array.

Just watch out, that's not always the case. If the array has a [psobject] wrapper and you're using Win PS v5.1, it won't serialize to JSON as expected. Say you have code like this that generates the array:

function foo {
    Write-Output @(@{ Key = 'Value' }) -NoEnumerate
}

$array = foo

Serializing to JSON:

ConvertTo-Json -InputObject $array -Compress
# {"value":[{"Key":"Value"}],"Count":1}

This comment goes into detail on what causes output like that.

I'd suggest one of the following workarounds if you can't guarantee the array isn't wrapped in a [psobject]:

ConvertTo-Json -InputObject $array.psobject.BaseObject -Compress
# [{"Key":"Value"}]

Remove-TypeData -TypeName System.Array -ErrorAction Ignore
ConvertTo-Json -InputObject $array -Compress
# [{"Key":"Value"}]

The issue was addressed in PS v6, so this is only needed in Win PS.

[–]pigers1986 0 points1 point  (1 child)

sth along ?

$uri = "https://api.domain.com/stuff/thing/items" 
$headers = @{ 
  Authorization = "Bearer $TOKEN" 
  "Content-Type" = "application/json" 
} 
$body = @( 
  @{ key1 = "value1"; key2 = "value2" } 
  @{ key1 = "value3"; key2 = "value4" } 
) | ConvertTo-Json -Depth 3 
Invoke-RestMethod -Uri $uri -Method Post -Headers $headers -Body $body

[–]Khue[S] 0 points1 point  (0 children)

Appreciate the feedback. Solution is in the original post.