ConvertTo-JSON adds Count and Value Property Names

Welcome Forums General PowerShell Q&A ConvertTo-JSON adds Count and Value Property Names

This topic contains 7 replies, has 4 voices, and was last updated by

 
Participant
2 years, 6 months ago.

  • Author
    Posts
  • #45672

    Participant
    Points: 21
    Rank: Member

    Hi Folks,

    I have to construct a JSON string containing some basics on my ESXi host and it's VMs.

    $esxihost = 'esxi-01'
    Connect-VIServer $esxihost
    
    Get-VMHost -Name $esxihost | Get-View | 
        Select @{N="host_cpus";E={($_.Hardware.CpuInfo.NumCpuPackages * $_.Hardware.CpuInfo.NumCpuCores)}},
            @{N="host_sn";E={(Get-EsxCli).hardware.platform.get().SerialNumber}},
            @{N="hostname";E={$_.Name}},
            @{N="vms";E={Get-VM |
                Select @{N="memory";E={[string]($_.MemoryMB)}},
                    @{N="name";E={$_.Name }}          
            }}| ConvertTo-Json -Depth 2
    

    This is the object before it's converted to JSON

    $object | GM
    
       TypeName: Selected.VMware.Vim.HostSystem
    
    Name        MemberType   Definition                                             
    ----        ----------   ----------                                             
    Equals      Method       bool Equals(System.Object obj)                         
    GetHashCode Method       int GetHashCode()                                      
    GetType     Method       type GetType()                                         
    ToString    Method       string ToString()                                      
    hostname    NoteProperty System.String hostname=ESXI-01.company.local
    host_cpus   NoteProperty System.Int32 host_cpus=40                              
    host_sn     NoteProperty System.String host_sn=8XXXX2                          
    vms         NoteProperty System.Object[] vms=                                  
    
    $object | fl
    
    host_cpus : 40
    host_sn   : 8XXXXX2
    hostname  : ESXI-01.company.local
    vms       : {@{memory=16384; name=DC-01}, @{memory=8192; name=test-centos}}
    

    Looks ok, but this is the converted JSON

    {
        "host_cpus":  40,
        "host_sn":  "8XXXXX2",
        "hostname":  "ESXI-01.company.local",
        "vms":  {
                    "value":  [
                                  {
                                      "memory":  "16384",
                                      "name":  "DC-01"
                                  },
                                  {
                                      "memory":  "8192",
                                      "name":  "test-centos"
                                  }
                              ],
                    "Count":  2
                }
    }
    

    My problem is the Value and Count properties are being added.

    The format I need to send the JSON doesn't allow these.

    {
        "host_cpus":  40,
        "host_sn":  "8XXXXX2",
        "hostname":  "ESXI-01.company.local",
        "vms":  {
                   [
                                  {
                                      "memory":  "16384",
                                      "name":  "DC-01"
                                  },
                                  {
                                      "memory":  "8192",
                                      "name":  "test-centos"
                                  }
                              ],
                }
    }
    
  • #45677

    Participant
    Points: 21
    Rank: Member

    I forgot to mention, I found this explanation from Trevor Sullivan but I can't get it to work for me.

    http://stackoverflow.com/questions/20848507/why-does-powershell-give-different-result-in-one-liner-than-two-liner-when-conve

    PowerShell automatically wraps multiple objects into a collection called a PSMemberSet that has a Count property on it. It's basically how PowerShell manages arbitrary arrays of objects. What's happening is that the Count property is getting added to the resulting JSON, yielding the undesirable results that you're seeing.

    You can work around this behavior by referencing the SyncRoot property of the PSMemberSet (which implements the ICollection .NET interface), and passing the value of that property to ConvertTo-Json.

  • #45680

    Keymaster
    Points: 1,785
    Helping HandTeam Member
    Rank: Community Hero

    I suspect asking Trevor might be the best solution. I've pinged him on Twitter, @pcgeek86.

  • #45684

    Member
    Points: 0
    Rank: Member

    What version of PowerShell are you running? With the latest version of WMF5 on Windows 10, I'm not seeing a problem when I tried to reproduce this, but I do remember there being some headaches with the JSON serializer in PSv4:

    $json = @'
    {
        "host_cpus":  40,
        "host_sn":  "8XXXXX2",
        "hostname":  "ESXI-01.company.local",
        "vms":                 [
                                  {
                                      "memory":  "16384",
                                      "name":  "DC-01"
                                  },
                                  {
                                      "memory":  "8192",
                                      "name":  "test-centos"
                                  }
                              ]
                }
    '@
    
    $json | ConvertFrom-Json | ConvertTo-Json -Depth 2
    
    < #
    {
        "host_cpus":  40,
        "host_sn":  "8XXXXX2",
        "hostname":  "ESXI-01.company.local",
        "vms":  [
                    {
                        "memory":  "16384",
                        "name":  "DC-01"
                    },
                    {
                        "memory":  "8192",
                        "name":  "test-centos"
                    }
                ]
    }
    #>
    
  • #45692

    Participant
    Points: 21
    Rank: Member

    Hi Dave,

    I'm on WMF5 but Windows 7 (5.0.10586.117).

    I will set-up a W12 R2 or W10 system with WMF5 and see if that works.

    Thanks,

    Michael

  • #45862

    Participant
    Points: 21
    Rank: Member

    Hi Guys,

    Yes hit the issue on Windows 2012 R2 and Windows 10.

    I involved one of my developer friends and he picked up on how to apply Trevor's recommendation to use syncroot. The issue appears when using Select-Object (which you have not run Dave).

    Get-VMHost -Name $esxihost | Get-View | 
        Select @{N="host_cpus";E={($_.Hardware.CpuInfo.NumCpuPackages * $_.Hardware.CpuInfo.NumCpuCores)}},
            @{N="host_sn";E={(Get-EsxCli).hardware.platform.get().SerialNumber}},
            @{N="hostname";E={$_.Name}},
            @{N="vms";E={Get-VM |
                Select @{N="memory";E={[string]($_.MemoryMB)}},
                    @{N="name";E={$_.Name }}                
            }}| % { $_.vms = $_.vms.syncroot; $_ } | ConvertTo-Json -Depth 2
    

    Regards,

    Michael

  • #45869

    Member
    Points: 0
    Rank: Member

    That is just weird. It's certainly a bug in ConvertTo-Json, but so far I haven't spotted the problem in the decompiled code. In any case, I was at least able to reproduce the problem.

  • #45907

    Participant
    Points: -19
    Rank: Member

    To expand on Michael's answer:

    * The problem is not OS-specific and has been around since PSv3.

    * The problem is not ConvertTo-Json; rather, Select-Object creates the problem when creating array-valued properties via a script block that functions as the expression entry of a hashtable-defined property. In short: the values of such properties are treated as a single object rather than as a collection.
    Here's a quick demonstration that ConvertTo-Json generally does handle array-valued properties properly:

    [pscustomobject] @{ host = 'somehost'; vms = 'vm1', 'vm2' } | ConvertTo-Json

    * The workaround is to force such array properties to behave like a regular collection, for which you can use the SyncRoot property, but it's not necessary; redefining the property with its value evaluated via $(...) is sufficient.

    * I've posted a generic (albeit non-recursive) workaround in this SO answer. Note that the linked answer focuses on a similar bug in ConvertFrom-Json (note: From-Json, not To-Json), which exhibits the same – presumably buggy – behavior when processing a JSON string that is an array. Also note that I believe Trevor's answer there to be incorrect.

The topic ‘ConvertTo-JSON adds Count and Value Property Names’ is closed to new replies.