0

I need to interact with an API that is expecting an array of objects, among other parameters. Example:

{
    "fields":  {
        "somefield": "somevalue",
        "someobject": {
            "name": "foobar"
        },
        "versions":  [
            {
                "name": "1.0"
            }
        ]
    }
}

With the help of this answer, I've tried two different ways of handling this. I've combined them into a single code example:

$versionName = New-Object -TypeName PSObject
$versionName | Add-Member -Name "name" -MemberType NoteProperty -Value "1.0"

$versionName2 = @{}
$versionName2.name = "1.0"

$postIssueBody = @{}
$postIssueBody.fields = @{}
$postIssueBody.fields.somefield = "somevalue"
$postIssueBody.fields.someobject = @{}
$postIssueBody.fields.someobject.name = "foobar"
$postIssueBody.fields.version = @($versionName)
$postIssueBody.fields.version2 = @()
$postIssueBody.fields.version2 += [pscustomobject]$versionName2

$postIssueRequestJson = $postIssueBody | ConvertTo-Json

$postIssueRequestJson

This results in the following output:

{
    "fields":  {
        "somefield": "somevalue",
        "someobject": {
            "name": "foobar"
        },
        "version":  [
            "@{name=1.0}"
        ],
        "version2":  [
            "@{name=1.0}"
        ]
    }
}

As you can see, that's not going to fly as valid JSON. What is the best way to handle this assignment so that the version names are properly formed after going through ConvertTo-Json?

Community
  • 1
  • 1
Ellesedil
  • 1,576
  • 1
  • 20
  • 44

2 Answers2

2

The ConvertTo-Json function has a switch called Depth. It informs the Convert function how deep it should go when converting data to the JSON format. By default, it is set at 2. Since the data that isn't being converted properly sits at a depth of 3, we simply need to set the depth to that, like so:

$postIssueRequestJson = $postIssueBody | ConvertTo-Json -Depth 3

And now we have well-formed JSON.

{
    "fields":  {
        "somefields":  "somevalue",
        "someobject":  {
            "name":  "foobar"
        },
        "versions":  [
            {
                "name":  "1.0"
            }
        ]
    }
}
Ellesedil
  • 1,576
  • 1
  • 20
  • 44
-1

Ok, I think I understand. So you need a string that starts with "versions": and is followed by an array of objects, yes? So, let's start with an empty array.

$Array = @()

Then we can create objects, and add those to the array:

$Array += [PSCustomObject]@{"Name1"="1.0.0"}
$Array += [PSCustomObject]@{"Name2"="3.10.0"}

Now we have a PowerShell array with PSCustomObjects in it. We can pipe that to ConvertTo-JSON and it will output:

[
    {
        "Name1":  "1.0.0"
    },
    {
        "Name2":  "3.10.0"
    }
]

Which is the array of objects you wanted. If you want an object to have that as it's value you could simply create another object to do that with:

$Versions = [PSCustomObject]@{'versions'=$Array}

Then you can convert that to JSON if you want and get:

{
    "versions":  [
                     {
                         "Name1":  "1.0.0"
                     },
                     {
                         "Name2":  "3.10.0"
                     }
                 ]
}

That's what you were looking for, right? Or if you really want it on one line:

PS C:\> ($Versions|convertto-json).split() -join ""

{"versions":[{"Name1":"1.0.0"},{"Name2":"3.10.0"}]}

To be formatted exactly as your first example we would have to get rid of the { } surrounding that result I suppose, you can do that with Trim() as such:

PS C:\> ($Versions|convertto-json).trim("{}").split() -join ""

"versions":[{"Name1":"1.0.0"},{"Name2":"3.10.0"}]

Edit: Ok, so you just need to add objects as property values of other objects as needed, much like I did for setting the array as a value in the object in my example.

I think the easiest way to understand what needs to be done is to take your example (minus the last comma, since that makes it throw errors), and pipe it into ConvertFrom-JSON and assign it a variable. Then you can see how that is formed in Powersehll. Once I do that (I named my variable $JSON), I can see that $JSON has 1 NoteProperty of 'fields'. That NoteProperty has 3 NoteProperties of 'somefield', 'someobject', and 'versions'. When I do a $JSON.fields|Get-Member I find out more about those.

somefield is just a String. That will be easy enough to deal with.

someobject is a PSCustomObject, basically a HashTable where name=foobar.

versions just shows that it's a System.Object, so I'll do $JSON.fields.versions.GetType() and it shows that the basetpe is System.Array. After looking at versions it looks like an array with 1 object in it, and that object has one noteproperty that is a string (like the first object we had).

So, there's two ways to go about doing this. You can either try and create your objects and array in-line, or you can make them ahead of time, starting at the deepest nested layer, and work your way up. I'll be showing you the later.

$name = [PSCustomObject]@{'name'='1.0'}
$versions=@($name)
$Someobject = [PSCustomObject]@{'name'='foobar'}
$Fields = [PSCustomObject]@{
        'somefields'='somevalue'
        'someobject'=$someobject
        'versions'=$versions}
$NewJSON = [PSCustomObject]@{'Fields'=$fields}
$NewJSON | ConvertTo-Json
TheMadTechnician
  • 34,906
  • 3
  • 42
  • 56
  • The one-line thing was only because I used block quotes and instead of code blocks. I don't actually care because I won't actually be outputting the JSON before sending it to the API. While I can get your example to work, as soon as I add it to the object I'm building for the entire body, it reverts back to the behavior I displayed in my question. I've edited the question to more clearly explain the output I need. – Ellesedil Aug 15 '14 at 21:36
  • This doesn't appear to work :( If I literally copy and paste your code and run it, everything looks fine except this: `"versions": [ "@{name=1.0}" ]` so, it looks like we're still at the same problem. Good call on writing out the JSON and using ConvertFrom-Json. Admittedly, that idea occurred to me while eating dinner, but I wasn't able to try it out. – Ellesedil Aug 16 '14 at 01:56
  • Ok, this is odd. If you add `Write-Output $Fields | ConvertTo-Json`, then the output is formatted correctly. As soon as you include `$Fields` in `$NewJSON` and convert that, then it reverts back to the `"@{name=1.0}"`. That doesn't make any sense... is this potentially a bug in Powershell? – Ellesedil Aug 16 '14 at 02:06
  • Found it. ConvertTo-Json has a switch called Depth. You need to include it on `$NewJSON`. By default, the depth is set to 2. So, we actually need `$NewJSON | ConvertTo-Json -Depth 3`. In fact, that's the only adjustment I need to make, even with my original code. – Ellesedil Aug 16 '14 at 02:24