Save hash table in PowerShell object notation (PSO

2019-01-06 23:10发布

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'}
    )            
}

5条回答
Fickle 薄情
2楼-- · 2019-01-06 23:52

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
查看更多
疯言疯语
3楼-- · 2019-01-06 23:52

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
查看更多
老娘就宠你
4楼-- · 2019-01-07 00:05

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.

查看更多
甜甜的少女心
5楼-- · 2019-01-07 00:09

I came to the same question.

To use the ConvertTo-JSON is indeed the most obvious work around but in earlier versions of PowerShell this Cmdlet is not available, besides why should you speak another language (e.g. JSON) if you do not want to interchange any data with another language? I found out that a ConvertTo-PSON Cmdlet will be nice to store data and also be very helpful in displaying, revealing and exploring the exact information of an object variable, so this is the result:

Function ConvertTo-PSON($Object, [Int]$Depth = 9, [Int]$Layers = 1, [Switch]$Strict, [Version]$Version = $PSVersionTable.PSVersion) {
    $Format = $Null
    $Quote = If ($Depth -le 0) {""} Else {""""}
    $Space = If ($Layers -le 0) {""} Else {" "}
    If ($Object -eq $Null) {"`$Null"} Else {
        $Type = "[" + $Object.GetType().Name + "]"
        $PSON = If ($Object -is "Array") {
            $Format = "@(", ",$Space", ")"
            If ($Depth -gt 1) {For ($i = 0; $i -lt $Object.Count; $i++) {ConvertTo-PSON $Object[$i] ($Depth - 1) ($Layers - 1) -Strict:$Strict}}
        } ElseIf ($Object -is "Xml") {
            $Type = "[Xml]"
            $String = New-Object System.IO.StringWriter
            $Object.Save($String)
            $Xml = "'" + ([String]$String).Replace("`'", "'") + "'"
            If ($Layers -le 0) {($Xml -Replace "\r\n\s*", "") -Replace "\s+", " "} ElseIf ($Layers -eq 1) {$Xml} Else {$Xml.Replace("`r`n", "`r`n`t")}
            $String.Dispose()
        } ElseIf ($Object -is "DateTime") {
            "$Quote$($Object.ToString('s'))$Quote"
        } ElseIf ($Object -is "String") {
            0..11 | ForEach {$Object = $Object.Replace([String]"```'""`0`a`b`f`n`r`t`v`$"[$_], ('`' + '`''"0abfnrtv$'[$_]))}; "$Quote$Object$Quote"
        } ElseIf ($Object -is "Boolean") {
            If ($Object) {"`$True"} Else {"`$False"}
        } ElseIf ($Object -is "Char") {
            If ($Strict) {[Int]$Object} Else {"$Quote$Object$Quote"}
        } ElseIf ($Object -is "ValueType") {
            $Object
        } ElseIf ($Object.Keys -ne $Null) {
            If ($Type -eq "[OrderedDictionary]") {$Type = "[Ordered]"}
            $Format = "@{", ";$Space", "}"
            If ($Depth -gt 1) {$Object.GetEnumerator() | ForEach {$_.Name + "$Space=$Space" + (ConvertTo-PSON $_.Value ($Depth - 1) ($Layers - 1) -Strict:$Strict)}}
        } ElseIf ($Object -is "Object") {
            If ($Version -le [Version]"2.0") {$Type = "New-Object PSObject -Property "}
            $Format = "@{", ";$Space", "}"
            If ($Depth -gt 1) {$Object.PSObject.Properties | ForEach {$_.Name + "$Space=$Space" + (ConvertTo-PSON $_.Value ($Depth - 1) ($Layers - 1) -Strict:$Strict)}}
        } Else {$Object}
        If ($Format) {
            $PSON = $Format[0] + (&{
                If (($Layers -le 1) -or ($PSON.Count -le 0)) {
                    $PSON -Join $Format[1]
                } Else {
                    ("`r`n" + ($PSON -Join "$($Format[1])`r`n")).Replace("`r`n", "`r`n`t") + "`r`n"
                }
            }) + $Format[2]
        }
        If ($Strict) {"$Type$PSON"} Else {"$PSON"}
    }
} Set-Alias PSON ConvertTo-PSON -Description "Convert variable to PSON"

I have nicknamed (aliased) it to simply PSON as a ConvertFrom-PSON cmdlet indeed already exist in the form of: Invoke-Expression:

Set-Alias ConvertFrom-PSON Invoke-Expression -Description "Convert variable from PSON"

It converts most (all?) of the native PowerShell object types which I have listed here in a single test object:

$Object = @{
    String    = [String]"Text"
    Char      = [Char]65
    Byte      = [Byte]66
    Int       = [Int]67
    Long      = [Long]68
    Null      = $Null
    Booleans  = $False, $True
    Decimal   = [Decimal]69
    Single    = [Single]70
    Double    = [Double]71
    DateTime  = [DateTime]"Monday, October 7, 1963 9:47:00 PM"
    Array     = @("One", "Two", @("Three", "Four"), "Five")
    HashTable = @{city="New York"; currency="Dollar (`$)"; postalCode=10021; Etc = @("Three", "Four", "Five")}
    Ordered   = [Ordered]@{One = 1; Two = 2; Three = 3; Four = 4}
    Object    = New-Object PSObject -Property @{Name = "One"; Value = 1; Text = @("First", "1st")}
}

To convert the object to PSON string you can simply give the command:

PSON $Object

This basic example will converts the object into a PowerShell string similar to the ConvertTo-JSON cmdlet which can be converted back again with the native Invoke-Expression PowerShell cmdlet.

Strict

The ConvertTo-PSON has also a –Strict option which prevents any type casting by explicitly adding the type names this will give a big advantage to the JSON format. This type casting will usually happen with the object type when to convert them:

Type                    JSON           PSON      PSON -Strict
----------------------- -------------- --------- ------------
String                  String         String    String
Char                    String         String    Char
Byte                    Int32          Int32     Byte
Int32 (Int)             Int32          Int32     Int32
Int64 (Long)            Int32          Int32     Int64
Null                    Null           Null      Null
Boolean                 Boolean        Boolean   Boolean
Decimal                 Int32          Int32     Decimal
Single                  Int32          Int32     Single
Double                  Int32          Int32     Double
DateTime                DateTime       DateTime  DateTime
Object[] (Array)        Object[]       Object[]  Object[]
HashTable               PSCustomObject HashTable HashTable
Ordered                 PSCustomObject HashTable Ordered
PSCustomObject (Object) PSCustomObject HashTable PSCustomObject
XML                     (TypeName)     String    XML

To store the object in e.g. a file, registry, database, etc. the best is to use the –Strict option and define 0 layers which will compress the data (remove all spaces):

$PSON1 = PSON -Layers 0 -Strict $Object 

Layers

The layers option will define how the layer levels will expanded over separate lines. Each layer level will be get an extra tab indent.

-Layers 0    One line without any spaces (compressed mode)

-Layers 1    One line formatted with spaces (default)

-Layers X    Multiple layers are expanded over multiple lines until a depth of X levels (Indents)

Let’s convert the PSON string back and confirm the results:

$Test1 = invoke-expression $PSON1      # ConvertFrom-PSON
$PSON2 = PSON -Layers 4 -Strict $Test1 # ConvertTo-PSON again in a better readable format
Write-Host $PSON2
$Test2 = invoke-expression $PSON2      #Confirm object structure is still intact

This is the result:

[Hashtable]@{
        Decimal = [Decimal]69;
        Long = [Int64]68;
        Char = [Char]65;
        Array = [Object[]]@(
                [String]"One",
                [String]"Two",
                [Object[]]@(
                        [String]"Three",
                        [String]"Four"
                ),
                [String]"Five"
        );
        Object = [PSCustomObject]@{
                Name = [String]"One";
                Value = [Int32]1;
                Text = [Object[]]@(
                        [String]"First",
                        [String]"1st"
                )
        };
        Int = [Int32]67;
        Byte = [Byte]66;
        HashTable = [Hashtable]@{
                postalCode = [Int32]10021;
                currency = [String]"Dollar`t(`$)";
                city = [String]"New York";
                Etc = [Object[]]@(
                        [String]"Three",
                        [String]"Four",
                        [String]"Five"
                )
        };
        Booleans = [Object[]]@(
                [Boolean]$False,
                [Boolean]$True
        );
        Null = $Null;
        String = [String]"Text";
        Ordered = [Ordered]@{
                One = [Int32]1;
                Two = [Int32]2;
                Three = [Int32]3;
                Four = [Int32]4
        };
        DateTime = [DateTime]"1963-10-07T21:47:00";
        Single = [Single]70;
        Double = [Double]71
}

Depth

The depth option is similar to the depth option in then ConvertTo-JSON command. By default the depth is limited to 9 levels. A depth of 0 levels will remove the double quotes from strings this might come in handy where you expect a string but would like to be alerted when it is not, e.g.:

Write-Host "User name:" (PSON $UserName 0)

Version

PowerShell 2.0 does not except the [PSCustomObject] type which will be replace with New-Object PSObject –Property when ran PowerShell 2.0, if you like to interchange data from a higher version of PowerShell with PowerShell 2.0 you can use the –Version 2.0 option.

Examples

Using the hashtable in the original question from Craig:

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

Write-Host (PSON $Craig) # Standard

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

Write-Host (PSON $Craig -Layers 0) # Compressed

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

Write-Host (PSON $Craig -Layers 3) # 3 Layers

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

Write-Host (PSON $Craig -Strict -Layers 9) # Strict, 9 (all) layers

[Hashtable]@{
        name = [String]"report 0";
        parameters = [Object[]]@(
                [Hashtable]@{
                        values = [Object[]]@(
                                [Int32]1,
                                [Int32]2,
                                [Int32]3,
                                [Int32]4
                        );
                        default = [Int32]1;
                        name = [String]"parameter 0"
                },
                [Hashtable]@{
                        values = [Object[]]@(
                                [String]"A",
                                [String]"B",
                                [String]"C"
                        );
                        default = [String]"A";
                        name = [String]"parameter 1"
                }
        )
}

For the latest see: ConvertTo-Expression at the PowerShell Gallery.

查看更多
够拽才男人
6楼-- · 2019-01-07 00:12

Found a solution to read/write hash tables to an INI file:

查看更多
登录 后发表回答