Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Hashtables from ConvertFrom-json have different type from powershells built-in hashtables, how do I make them the same?

I have a json file (test.json) that looks something like this:

{
    "root":
    {
        "key":"value"
    }
}

I'm loading it into powershell using something like this:

PS > $data = [System.String]::Join("", [System.IO.File]::ReadAllLines("test.json")) | ConvertFrom-Json

root
----
@{key=value}

I'd like to be able to enumerate the keys of the 'hashtable' like object that is defined by the json file. So, Ideally, I'd like to be able to do something like:

$data.root.Keys

and get ["key"] back. I can do this with the built-in hashtables in powershell, but doing this with a hashtable loaded from json is less obvious.

In troubleshooting this, I've noticed that the fields returned by ConvertFrom-json have different types than those of Powershell's hashtables. For example, calling .GetType() on a built-in hashtable makes shows that it's of type 'Hashtable':

PS > $h = @{"a"=1;"b"=2;"c"=3}
PS > $h.GetType()

IsPublic IsSerial Name                                     BaseType
-------- -------- ----                                     --------
True     True     Hashtable                                System.Object

doing the same for my json object yields PSCustomObject:

PS > $data.root.GetType()

IsPublic IsSerial Name                                     BaseType
-------- -------- ----                                     --------
True     False    PSCustomObject                           System.Object

Is there any way to cast or transform this object into a typical powershell Hashtable?

like image 379
brad Avatar asked Feb 25 '14 01:02

brad


2 Answers

Here's a quick function to convert a PSObject back into a hashtable (with support for nested objects; intended for use with DSC ConfigurationData, but can be used wherever you need it).

function ConvertPSObjectToHashtable
{
    param (
        [Parameter(ValueFromPipeline)]
        $InputObject
    )

    process
    {
        if ($null -eq $InputObject) { return $null }

        if ($InputObject -is [System.Collections.IEnumerable] -and $InputObject -isnot [string])
        {
            $collection = @(
                foreach ($object in $InputObject) { ConvertPSObjectToHashtable $object }
            )

            Write-Output -NoEnumerate $collection
        }
        elseif ($InputObject -is [psobject])
        {
            $hash = @{}

            foreach ($property in $InputObject.PSObject.Properties)
            {
                $hash[$property.Name] = ConvertPSObjectToHashtable $property.Value
            }

            $hash
        }
        else
        {
            $InputObject
        }
    }
}
like image 199
Dave Wyatt Avatar answered Sep 19 '22 21:09

Dave Wyatt


The ConvertFrom-Json cmdlet gives you a custom object so you have to access them using dot notation rather than as a subscript. Usually you would know what fields you expect in the JSON so this is actually more useful in general than getting a hash table. Rather than fighting the system by converting back to a hash table, I suggest you work with it.

You can use select with wildcard property names to get at the properties:

PS D:\> $data = @"
{
    "root":
    {
        "key":"value", "key2":"value2", "another":42
    }
}
"@ | ConvertFrom-Json

PS D:\> $data.root | select * | ft -AutoSize

key   key2   another
---   ----   -------
value value2      42



PS D:\> $data.root | select k* | ft -AutoSize

key   key2  
---   ----  
value value2

and Get-Member if you want to extract a list of property names that you can iterate over:

PS D:\> ($data.root | Get-Member -MemberType NoteProperty).Name
another
key
key2

Putting that into a loop gives code like this:

PS D:\> foreach ($k in ($data.root | Get-Member k* -MemberType NoteProperty).Name) {
    Write-Output "$k = $($data.root.$k)"
    }
key = value
key2 = value2
like image 24
Duncan Avatar answered Sep 22 '22 21:09

Duncan