Building PowerShell Arrays and Hashtables for JSON Payloads

While tinkering around with Slack’s Incoming Webhooks feature, I came upon the ability to add message attachments. This seemed like a nifty way to condense a few different pieces of information into a rich formatted Slack message with a bit of PowerShell code. The challenge is converting the syntax for arrays and key-value pairs (hashtables) from native JSON to native PowerShell.

I prefer to build using PowerShell objects, convert to JSON, and then ship the results off to a URI, rather than editing native JSON directly in PowerShell. This is cleaner looking, easier to manipulate while writing the code, and avoids any double-conversion losses when toggling back-and-forth between PowerShell objects and a JSON string.

An array of gummy bears!
An array of gummy bears!

I’ve found that most articles covering JSON assume you already have a perfect grasp of the syntax, rather than going into what’s going on with the code. This post will cover some of the basics of reading JSON, including how to format the strings so that they make sense in PowerShell, so that you can build your own scripts.

Arrays and Hashtables in PowerShell

Put simply, an array is a collection of objects. Below, I’ve created an array by storing three strings into the $body variable. The parenthesis is used to build arrays.

$body = @(
    'value1','value2','value3'
)

Hashtables, also known as key-value pairs, are a bit different. As the name implies, this is a collection of keys that describe an object. Each key has a value. This makes looking up data trivial. Here’s an example, and note that the curly brace is used to build hashtables.

$body = @{
    key1 = 'value1'
    key2 = 'value2'
    key3 = 'value3'
}

Each type of collection has various pros and cons. With a hashtable, each key must be a unique object; you can’t re-use the same key name within a single hashtable. But it’s really easy to lookup data based on a key and return the value. Arrays, on the other hand, do not care about duplicated data, but are somewhat limited when trying to add or search for items in the array (compared to a hashtable).

Arrays and Hashtables in JSON

The syntax for arrays and hashtables is a bit different in JSON. Let’s start by looking at an array of objects.

[
    "value1",
    "value2",
    "value3"
]

Note the use of a square bracket instead of PowerShell’s “at” symbol followed by a pair of parenthesis. It’s also considered cleaner to express each object in the array on its own line. Otherwise, the code looks about the same.

Hashtables look quite a bit different. The data is enclosed in curly braces, without an “at” symbol. Each key and value are separated by a colon, with a trailing comma separating the objects.

{
    "key3":  "value3",
    "key1":  "value1",
    "key2":  "value2"
}

Now that you know what each language’s syntax looks like, let’s build some PowerShell objects that can be converted to JSON for making calls to endpoints expecting payloads in that format.

The Easy Parts

Building a JSON string out of nested PowerShell hashtables is relatively simple. Below, I’ve created a more complex example than what we shown previously. It’s a hashtable that contains some keys and values, a nested hashtable, and a nested array.

$body = @{
    key1 = 'value1'
    key2 = 'value2'
    key3 = 'value3'
    key4 = @(
        'value1', 'value2', 'value3'
    )
    key5 = 'value5'
    key6 = @{
        key7 = 'value7'
        key8 = 'value9'
        key9 = 'value8'
    }
}

Keys 1, 2, 3, and 5 are simply key-value pairs in the root hashtable. Key 4 contains an array of values – in this case three strings (value1, value2, and value3). Key 6 is a nested hashtable, meaning it’s a hashtable inside of a hashtable, and it contains key 7, key 8, and key 9.

Here’s what this what look like in JSON format.

{
    "key5":  "value5",
    "key4":  [
                 "value1",
                 "value2",
                 "value3"
             ],
    "key1":  "value1",
    "key3":  "value3",
    "key6":  {
                 "key9":  "value8",
                 "key8":  "value9",
                 "key7":  "value7"
             },
    "key2":  "value2"
}

Now that you know what each format looks like, it should be easier to translate JSON requirements into PowerShell code. Alternatively, you can always take the “cheap way out” and drop the entire JSON string into a pair of “at” symbols with paired double quotes. Like so:

$body = @"
{
    "key5":  "value5",
    "key4":  [
                 "value1",
                 "value2",
                 "value3"
             ],
    "key1":  "value1",
    "key3":  "value3",
    "key6":  {
                 "key9":  "value8",
                 "key8":  "value9",
                 "key7":  "value7"
             },
    "key2":  "value2"
}

"@

That’s really ugly! Part of the magic of PowerShell is working with objects, not manipulating strings. This method also requires a lot of escaping to insert variables.

The Tricky Parts

Where things can get a little more challenging is when you see requirements like this one from Slack:

{
    "attachments": [
        {
            "fallback": "ReferenceError - UI is not defined: https://honeybadger.io/path/to/event/",
            "text": "<https://honeybadger.io/path/to/event/|ReferenceError> - UI is not defined",
            "fields": [
                {
                    "title": "Project",
                    "value": "Awesome Project",
                    "short": true
                },
                {
                    "title": "Environment",
                    "value": "production",
                    "short": true
                }
            ],
            "color": "#F35A00"
        }
    ]
}

Note that various blends of arrays and hashtables? The syntax alone can be an adventure! Let’s see what this would look like in PowerShell:

$body = @{
    attachments = @(
        @{
            fallback = 'ReferenceError - UI is not defined: https://honeybadger.io/path/to/event/'
            text = '<https://honeybadger.io/path/to/event/|ReferenceError> - UI is not defined'
            fields = @(
                @{
                    title = 'Project'
                    value = 'Awesome Project'
                    short = 'true'
                },
                @{
                    title = 'Environment'
                    value = 'production'
                    short = 'true'
                }
            )
            color = '#F35A00'
        }
    )
}

There’s some handy projects on the web that will do this for you, but I like knowing how to do things by hand so that I understand what’s going on with the code. In this case, I’ve just gone through the JSON and changed the array and hashtable syntax into PowerShell code. Note that the fields section is an array of hashtables and arrays require a comma between each object.

Next, let’s convert the object into JSON by using ConvertTo-Json.

$body | ConvertTo-Json
{
    "attachments":  [
                        {
                            "color":  "#F35A00",
                            "fields":  "System.Collections.Hashtable System.Collections.Hashtable",
                            "fallback":  "ReferenceError - UI is not defined: https://honeybadger.io/path/to/event/",
                            "text":  "\u003chttps://honeybadger.io/path/to/event/|ReferenceError\u003e - UI is not defined"

                        }
                    ]
}

Ut oh! Why does the fields key has a value of two System.Collections.Hashtable object names? That’s not right …

Gotcha! JSON Conversion Depth

The ConvertTo-Json cmdlet has a parameter named Depth. It can be used to go deeper into the PowerShell objects and expand what is put into the JSON string. Because the previous example had some deeply nested information, the conversion cmdlet stopped at the fields key and didn’t expand the hashtable values.

To remedy this, use the Depth parameter with an integer specifying how many layers down to explore. I’ll repeat the previous command with Depth set to 4 layers.

$body | ConvertTo-Json -Depth 4
{
    "attachments":  [
                        {
                            "color":  "#F35A00",
                            "fields":  [
                                           {
                                               "short":  "true",
                                               "title":  "Project",
                                               "value":  "Awesome Project"
                                           },
                                           {
                                               "short":  "true",
                                               "title":  "Environment",
                                               "value":  "production"
                                           }
                                       ],
                            "fallback":  "ReferenceError - UI is not defined: https://honeybadger.io/path/to/event/",
                            "text":  "\u003chttps://honeybadger.io/path/to/event/|ReferenceError\u003e - UI is not defined"

                        }
                    ]
}

There! Now all of the objects have been crawled and turned into a proper JSON string. This should solve most of the common issues with working with JSON in PowerShell, and I hope you’ve learned a few handy tricks when it comes to syntax for arrays and hashtables.