18

The question Loading a PowerShell hashtable from a file? documents how to load a file that contains a hashtable in PSON format into a variable, but how does one save a hashtable to a file in PSON format?

Hashtable:

@{            
 "name" = "report 0"            
 "parameters" = @(
    @{"name" = "parameter 0"; "default" = 1; "values"=1,2,3,4},
    @{"name" = "parameter 1"; "default" = 'A'; "values" = 'A','B','C'}
    )            
}
2
  • 1
    There is nothing like this built-in. Presumably you have to write your own solution but it's a tough task unless you are going to allow only limited subset of data types, no recursion in nested objects, etc. Commented Mar 1, 2013 at 10:26
  • Excellent question, but I'm curious as to where the term PSON came from; it seems that it never really caught on. More precisely, it is a hashtable literal notation (which since v3 forms part of the syntactic sugar for custom-object construction; e.g. [pscustomobject] @{name="foo";age=21}, but you cannot use that in data files loaded with Import-PowerShellDataFile, which only support hashtable literals) Commented Aug 30, 2022 at 18:22

4 Answers 4

15

After 5 years, the cmdlet I had pasted in the original answer has undergone so many updates that it has become completely outdated. Therefore I have replaced the code and the ReadMe with a link to the latest version.

ConvertTo-Expression

The ConvertTo-Expression cmdlet can be download from PowerShell Gallery using the command:

Install-Script -Name ConvertTo-Expression

ReadMe

The full ReadMe (and source code) is available from the GitHub

Answer

Below are some possible options to serialize the specific example (assigned to $Craig) in the question:

ConvertTo-Expression $Craig
@{
    parameters =
        @{
            name = 'parameter 0'
            default = 1
            values =
                1,
                2,
                3,
                4
        },
        @{
            name = 'parameter 1'
            default = 'A'
            values =
                'A',
                'B',
                'C'
        }
    name = 'report 0'
}

To limit the tree view expansion:
(Expand -0 will output a single line and Expand -1 will remove also the unnecessary spaces)

ConvertTo-Expression $Craig -expand 3
@{
    parameters =
        @{name = 'parameter 0'; default = 1; values = 1, 2, 3, 4},
        @{name = 'parameter 1'; default = 'A'; values = 'A', 'B', 'C'}
    name = 'report 0'
}

Preserving the explicit types (strong typed):

ConvertTo-Expression $Craig -expand 3 -Strong
[hashtable]@{
    parameters = [array](
        [hashtable]@{name = [string]'parameter 0'; default = [int]1; values = [array]([int]1, [int]2, [int]3, [int]4)},
        [hashtable]@{name = [string]'parameter 1'; default = [string]'A'; values = [array]([string]'A', [string]'B', [string]'C')}
    )
    name = [string]'report 0'
}

(Note: As per PowerShell design, HashTables are not in order, but if required you might use the [Ordered] type instead.)

Sign up to request clarification or add additional context in comments.

4 Comments

Or got to an elevated powershell and Install-Script ConvertTo-Expression -Scope AllUsers which places the script into $env:ProgramFiles\WindowsPowerShell\Scripts (which might not exit if this is your first script). That adds C:\Program Files\WindowsPowerShell\Scripts (typical path) to your $env:path but only elevated. Back to standard ps and do $env:Path = "$env:Path$env:ProgramFiles\WindowsPowerShell\Scripts;" then just . ConvertTo-Expression.ps1 in any prompt or script you want to use it. Call function as ConvertTo-Expression ...
[sorry editing time ran out]. Better in the elevated powershell, after Install-Script to do [Environment]::SetEnvironmentVariable('path',$env:path,'machine') to add the ...\WindowsPowerShell\Scripts dir to the path for the whole system, elevated and non elevated PS and everything else.
Also the ReadMe link is incorrect (and suggested edit queue is full). Easy to find: github.com/iRon7/ConvertTo-Expression#readme
@john v kumpf, I have changed the embedded function to cmdlet script which makes it easier installable.
11

Try the *-CliXml cmdlets. To save the object:

@{            
 "name" = "report 0"            
 "parameters" = @(
    @{"name" = "parameter 0"; "default" = 1; "values"=1,2,3,4},
    @{"name" = "parameter 1"; "default" = 'A'; "values" = 'A','B','C'}
    )            
} | Export-Clixml -Path c:\hash.xml

To read it back:

Import-Clixml c:\hash.xml

5 Comments

I would prefer PSON, as it is easier to read and edit than the XML that is generated by Import-Clixml.
If it's a matter of "readability", V3 lets you convert to JSON, which might actually be better.
Yes, I did read that. Unfortunately, my client is still on Windows XP, which doesn't support v2.
IIRC the clixml cmdlets introduced in v1 but I might be wrong
@craig This would be the proper, built-in way to do it, if there wasn't anybody like iRon to build a custom solution for you (which might very likely still have a lot of unknown issues.)
3

One way would be to put the hashtable definition in a scriptblock:

$hashtable = {
  @{            
    "name" = "report 0"            
    "parameters" = @(
        @{"name" = "parameter 0"; "default" = 1; "values"=1,2,3,4},
        @{"name" = "parameter 1"; "default" = 'A'; "values" = 'A','B','C'}
        )            
    }
}

$hashtable.tostring()

@{
"name" = "report 0"
"parameters" = @( @{"name" = "parameter 0"; "default" = 1; "values"=1,2,3,4}, @{"name" = "parameter 1"; "default" = 'A'; "values" = 'A','B','C'} )
}

Within the script, you'd need to invoke the script block to instantiate the hashtable:

$hash = .$hashtable

1 Comment

Correct me if I'm wrong, but it doesn't seem like I'd be able to load changes to the hashtable from a file if it is embedded in a script block.
0

How to use a shorthand "object notation" to generate an object in PowerShell:

$object = New-Object -TypeName PSObject -Property @{name="foo";age=21}

DISCLAIMER: I know this does not answer OP's question directly but it might help folks like me searching for a very similar issue and landing here.

1 Comment

Since v3 there's syntactic sugar that is not only more concise, but also more efficient, in addition to preserving the property order: [pscustomobject] @{name="foo";age=21}

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.