1

I am working to built a function to construct a json payload for an API call. The json payload will have a main "member", but I need to add a sub-member which is really a json array. I have this almost working, but I have been unable to get it completely working.

Here are two examples (which produce different -but incorrect- results):

This example produces the correct structure (except for the required opening and closing {} around the array:

[PSCustomObject] $contactMain = @{}
[PSCustomObject] $contactDetail_Native = @{}
[array] $contactDetail_Custom = @()

$contactDetail_Custom = `
    [PSCustomObject]@{
        field = '1'
        value = '1001'
    }

$contactDetail_Custom += $contactDetail_Custom

$contactDetail_Custom = `
    [PSCustomObject]@{
        field = '2'
        value = '1002'
    }

$contactDetail_Custom += $contactDetail_Custom

$contactDetail_Native = `
    [ordered]@{
        email     = 'brucebanner@myfakeemail.com'
        lastName  = 'Banner'
        firstName = 'Bruce'
    }

$contactDetail_Native.Add('fieldValues', $contactDetail_Custom)

$contactMain | Add-Member -MemberType NoteProperty -Name 'contact' -Value $contactDetail_Native -Force
#$contactMain | Add-Member -MemberType NoteProperty -Name 'fieldValues' -Value $contactDetail_Custom -Force

$contactMain = $contactMain | ConvertTo-Json
$contactMain

This example renders the values, but fieldValues needs to be inside the contacts member:

[PSCustomObject] $contactMain = @{}
[PSCustomObject] $contactDetail_Native = @{}
[array] $contactDetail_Custom = @()

$contactDetail_Custom = `
    [PSCustomObject]@{
        field = '1'
        value = '1001'
    }

$contactDetail_Custom += $contactDetail_Custom

$contactDetail_Custom = `
    [PSCustomObject]@{
        field = '2'
        value = '1002'
    }

$contactDetail_Custom += $contactDetail_Custom

$contactDetail_Native = `
    [ordered]@{
        email     = 'brucebanner@myfakeemail.com'
        lastName  = 'Banner'
        firstName = 'Bruce'
    }

#$contactDetail_Native.Add('fieldValues', $contactDetail_Custom)

$contactMain | Add-Member -MemberType NoteProperty -Name 'contact' -Value $contactDetail_Native -Force
$contactMain | Add-Member -MemberType NoteProperty -Name 'fieldValues' -Value $contactDetail_Custom -Force

$contactMain = $contactMain | ConvertTo-Json
$contactMain

The way I need it to render is like this:

{"contact": {
    "firstName":  "Bruce",
    "lastName":  "Banner",
    "email":  "brucebanner@myfakeemail.com",
    "fieldValues":  [
                        {
                            "field":  "1",
                            "value":  "1001"
                        },
                        {
                            "field":  "2",
                            "value":  "1002"
                        }
                    ]
}}

What am I doing wrong?

Thanks!

James
  • 75
  • 2
  • 8
  • [try avoid using the increase assignment operator (`+=`) to create a collection](https://stackoverflow.com/a/60708579/1701026) as it is exponentially expensive. – iRon Jun 07 '23 at 06:47
  • 1
    Thanks iRon - I had no idea about that. Seems like the current standard everywhere I found was to use +=. I'll definitely check out this preferred method. – James Jun 16 '23 at 17:58

2 Answers2

0

You might want to use this ConvertTo-Expression to build a template and/or get an impression of the PowerShell structure:

Install-Script -Name ConvertTo-Expression
$YourJson |ConvertFrom-Json |ConvertTo-Expression
[pscustomobject]@{contact = [pscustomobject]@{
        firstName = 'Bruce'
        lastName = 'Banner'
        email = 'brucebanner@myfakeemail.com'
        fieldValues =
            [pscustomobject]@{
                field = '1'
                value = '1001'
            },
            [pscustomobject]@{
                field = '2'
                value = '1002'
            }
    }}

To automatically build the fieldValues, you could do something like this:

function Get-CustomDetail {
    param([Parameter(ValueFromPipeline=$true)][String]$Field)
    process {
        [pscustomobject]@{
            field = $Field
            value = "100$Field"
        }
    }
}

[pscustomobject]@{contact = [pscustomobject]@{
        firstName = 'Bruce'
        lastName = 'Banner'
        email = 'brucebanner@myfakeemail.com'
        fieldValues = @(1,2 |Get-CustomDetail) # @(...) forces an array for single items
    }
} |ConvertTo-Json -Depth 9
{
  "contact": {
    "firstName": "Bruce",
    "lastName": "Banner",
    "email": "brucebanner@myfakeemail.com",
    "fieldValues": [
      {
        "field": "1",
        "value": "1001"
      },
      {
        "field": "2",
        "value": "1002"
      }
    ]
  }
}
iRon
  • 20,463
  • 10
  • 53
  • 79
0

Looks like I was able to figure this out on my own. The solution was to set the Depth parameter on the ConvertTo-Json cmdlet using the first code example I provided.

Here is the final version that produces the result I need:

[PSCustomObject] $contactMain          = @{}
[PSCustomObject] $contactDetail_Native = @{}
[array]          $contactDetail_Custom = @{}
[array]          $contactDetail        = @()


$contactDetail_Custom = `
    [PSCustomObject]@{
        field = '1'
        value = '1001'
    }

$contactDetail += $contactDetail_Custom

$contactDetail_Custom = `
    [PSCustomObject]@{
        field = '2'
        value = '1002'
    }

$contactDetail += $contactDetail_Custom

$contactDetail_Native = `
    [ordered]@{
        email     = 'brucebanner@myfakeemail.com'
        lastName  = 'Banner'
        firstName = 'Bruce'
    }

$contactDetail_Native.Add('fieldValues', $contactDetail)

$contactMain | Add-Member -MemberType NoteProperty -Name 'contact' -Value $contactDetail_Native -Force

$contactMain = $contactMain | ConvertTo-Json -Depth 100
$contactMain
James
  • 75
  • 2
  • 8