85

What is the easiest way to convert a PSCustomObject to a Hashtable? It displays just like one with the splat operator, curly braces and what appear to be key value pairs. When I try to cast it to [Hashtable] it doesn't work. I also tried .toString() and the assigned variable says its a string but displays nothing - any ideas?

chazbot7
  • 558
  • 2
  • 10
  • 32
alphadev
  • 1,469
  • 5
  • 18
  • 20
  • 1
    PSCustomObjects have advantages over hashtables. Think twice before converting it. http://stackoverflow.com/questions/22002748/hashtables-from-convertfrom-json-have-different-type-from-powershells-built-in-h/22010290#22010290 – spuder Dec 09 '16 at 17:38
  • 3
    Splatting doesn't work with a PSCustomObject, is a good reason I can think of. – Brain2000 Jan 11 '19 at 01:32

8 Answers8

110

Shouldn't be too hard. Something like this should do the trick:

# Create a PSCustomObject (ironically using a hashtable)
$ht1 = @{ A = 'a'; B = 'b'; DateTime = Get-Date }
$theObject = new-object psobject -Property $ht1

# Convert the PSCustomObject back to a hashtable
$ht2 = @{}
$theObject.psobject.properties | Foreach { $ht2[$_.Name] = $_.Value }
jpaugh
  • 6,117
  • 4
  • 36
  • 87
Keith Hill
  • 184,219
  • 38
  • 329
  • 358
  • 1
    Note that `$_.Name` is already a string, so `$ht2[$_.Name]` or `$h.($_.Name)` will work just as well as `"$($_.Name)"`. – Emperor XLII Jun 05 '11 at 14:53
  • 17
    Note that this doesn't work for PSCustomObjects created by `ConvertFrom-Json`. [This question](http://stackoverflow.com/q/22002748/310446) addresses that issue. – BenV Jan 16 '15 at 23:22
  • 4
    @BenV: Just to clarify: The problem stems from _nested_ custom objects, not from use of `ConvertFrom-Json` per se, which also produces `[PSCustomObject]` instances. In other words: a JSON source that produces non-nested objects works just fine; e.g.: `('{ "foo": "bar" }' | ConvertFrom-Json).psobject.properties | % { $ht = @{} } { $ht[$_.Name] = $_.Value } { $ht }` – mklement0 Jan 20 '16 at 19:59
  • 2
    Casting could become a reality in the future: https://connect.microsoft.com/PowerShell/feedback/details/679841/allow-casting-of-psobject-or-object-to-hashtable – W1M0R Mar 08 '16 at 11:22
  • See below an answer from @Svyatoslav Pidgorny which is using new features in PowerShell 6 or 7 for more simple approach! https://stackoverflow.com/a/61742479/3425553 – Igor May 27 '20 at 10:24
36

Keith already gave you the answer, this is just another way of doing the same with a one-liner:

$psobject.psobject.properties | foreach -begin {$h=@{}} -process {$h."$($_.Name)" = $_.Value} -end {$h}
Shay Levy
  • 114,369
  • 30
  • 175
  • 198
  • Heh, started with something very similar except that it was just long enough to invoke the SO horizontal scrollbar. BTW I think your `$'s` are missing some `_'s`. :-) – Keith Hill Sep 18 '10 at 16:13
  • That's what I was trying to avoid and eventually it swallowed the underscore sign. Thanks! – Shay Levy Sep 19 '10 at 15:56
  • @ShayLevy: What is the advantage of putting everything on the same line? – Rubanov May 11 '15 at 13:30
  • 3
    Nice; if you use `%` and positional parameters as the blocks, you can shorten to `$psobject.psobject.properties | % { $ht = @{} } { $ht[$_.Name] = $_.Value } { $ht }`. @Rubanov: It doesn't have to be on a single _line_, but the advantage is that a single _statement_ (pipeline) creates the hashtable. – mklement0 Jan 20 '16 at 19:41
30

Here's a version that works with nested hashtables / arrays as well (which is useful if you're trying to do this with DSC ConfigurationData):

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
        }
    }
}
Dave Wyatt
  • 1,359
  • 13
  • 8
  • 3
    This is the only version that worked for my data with multi-level nested objects and arrays. – Jeffrey Harmon Feb 09 '16 at 12:55
  • 3
    Excellent & elegant solution for the multi-level nested objects. – Petru Zaharia Sep 21 '16 at 00:33
  • As noted in a previous answer's comment, this code above handles complex / nested hashtables and is great for handling content from `ConvertFrom-Json`. See also [this question](http://stackoverflow.com/q/22002748/310446) – Thomas B in BDX May 09 '20 at 12:27
  • I am unable to get this to work "as-is" for nested objects: `@{ Name = "test1"; nested = @{ license = 'x'; cert = 'y' } } | Convert-PSObjectToHashTable` Instead, I had to add a `GetEnumerator()` on Line 15: `foreach ($object in $InputObject.GetEnumerator()) { ConvertPSObjectToHashtable $object }` – Keith S Garner Jul 13 '20 at 04:15
  • The same code by Adam Bertram can be found here: https://4sysops.com/archives/convert-json-to-a-powershell-hash-table/ – Ciove Aug 18 '20 at 10:32
  • This worked for me, but not if the $InputObject already was, or contained, a HashTable or Ordered Dictionary. I added the following just after `if ($null -eq $InputObject...` - add this line: `if ($InputObject -is [Hashtable] -or $InputObject.GetType().Name -eq 'OrderedDictionary') { return $InputObject }` – PSaul Jan 27 '22 at 22:48
18

My extremely lazy approach, enabled by a new feature in PowerShell 6:

$myhashtable = $mypscustomobject | ConvertTo-Json | ConvertFrom-Json -AsHashTable
3

This works for PSCustomObjects created by ConvertFrom_Json.

Function ConvertConvertFrom-JsonPSCustomObjectToHash($obj)
{
    $hash = @{}
     $obj | Get-Member -MemberType Properties | SELECT -exp "Name" | % {
                $hash[$_] = ($obj | SELECT -exp $_)
      }
      $hash
}

Disclaimer: I barely understand PowerShell so this is probably not as clean as it could be. But it works (for one level only).

mhenry1384
  • 7,332
  • 5
  • 52
  • 73
  • 1
    Little more cleaner (may be tougher to understand) `$hash=@{};$obj | Get-Member -MemberType Properties | foreach { $hash.Add($_.Name,$obj.($_.Name))}` – Adarsha May 17 '17 at 02:38
1

My code:

function PSCustomObjectConvertToHashtable() {
    param(
        [Parameter(ValueFromPipeline)]
        $object
    )

    if ( $object -eq $null ) { return $null }

    if ( $object -is [psobject] ) {
        $result = @{}
        $items = $object | Get-Member -MemberType NoteProperty
        foreach( $item in $items ) {
            $key = $item.Name
            $value = PSCustomObjectConvertToHashtable -object $object.$key
            $result.Add($key, $value)
        }
        return $result
    } elseif ($object -is [array]) {
        $result = [object[]]::new($object.Count)
        for ($i = 0; $i -lt $object.Count; $i++) {
            $result[$i] = (PSCustomObjectConvertToHashtable -object $object[$i])
        }
        return ,$result
    } else {
        return $object
    }
}
Hu Xinlong
  • 27
  • 4
0

Today, the "easiest way" to convert PSCustomObject to Hashtable would be so:

$custom_obj | ConvertTo-HashtableFromPsCustomObject    

OR

[hashtable]$custom_obj

Conversely, you can convert a Hashtable to PSCustomObject using:

[PSCustomObject]$hash_table

Only snag is, these nifty options may not be available in older versions of PS

Ifedi Okonkwo
  • 3,108
  • 4
  • 25
  • 44
0

For simple [PSCustomObject] to [Hashtable] conversion Keith's Answer works best.

However if you need more options you can use


function ConvertTo-Hashtable {
    <#
    .Synopsis
        Converts an object to a hashtable
    .DESCRIPTION
        PowerShell v4 seems to have trouble casting some objects to Hashtable.
        This function is a workaround to convert PS Objects to [Hashtable]
    .LINK
        https://github.com/alainQtec/.files/blob/main/src/scripts/Converters/ConvertTo-Hashtable.ps1
    .NOTES
        Base ref: https://community.idera.com/database-tools/powershell/powertips/b/tips/posts/turning-objects-into-hash-tables-2
    #>
    PARAM(
        # The object to convert to a hashtable
        [Parameter(ValueFromPipeline = $true, Mandatory = $true)]
        $InputObject,

        # Forces the values to be strings and converts them by running them through Out-String
        [switch]$AsString,

        # If set, empty properties are Included
        [switch]$AllowNulls,

        # Make each hashtable to have it's own set of properties, otherwise,
        # (default) each InputObject is normalized to the properties on the first object in the pipeline
        [switch]$DontNormalize
    )
    BEGIN {
        $headers = @()
    }
    PROCESS {
        if (!$headers -or $DontNormalize) {
            $headers = $InputObject | Get-Member -type Properties | Select-Object -expand name
        }
        $OutputHash = @{}
        if ($AsString) {
            foreach ($col in $headers) {
                if ($AllowNulls -or ($InputObject.$col -is [bool] -or ($InputObject.$col))) {
                    $OutputHash.$col = $InputObject.$col | Out-String -Width 9999 | ForEach-Object { $_.Trim() }
                }
            }
        } else {
            foreach ($col in $headers) {
                if ($AllowNulls -or ($InputObject.$col -is [bool] -or ($InputObject.$col))) {
                    $OutputHash.$col = $InputObject.$col
                }
            }
        }
    }
    END {
        return $OutputHash
    }
}

Maybe this is overkill but I hope it Helps

alainQtec
  • 21
  • 5