Powershell - Retain the text of all Enum properties with ConvertTo-Json

前端 未结 2 914
情深已故
情深已故 2020-12-16 19:11

For \"Get-Msoldomain\" powershell command-let I get the below output (lets call it Output#1) where Name, Status and Authentication are the property names a

相关标签:
2条回答
  • 2020-12-16 19:33

    Well, if you don't mind to take a little trip :) you can convert it to CSV which will force the string output, then re-convert it back from CSV to PS Object, then finally back to Json.

    Like this:

    Get-MsolDomain | ConvertTo-Csv | ConvertFrom-Csv | ConvertTo-Json
    
    • If you need to keep the original Types instead of converting it all to string see mklement0 helpful answer...
    0 讨论(0)
  • 2020-12-16 19:48

    PowerShell Core offers a simple solution via ConvertTo-Json's -EnumsAsStrings switch.

    GetMsolDomain | ConvertTo-Json -EnumsAsStrings  # PS *Core* only
    

    Unfortunately, this switch isn't supported in Windows PowerShell.

    Avshalom's answer provides a quick workaround that comes with a big caveat, however: All property values are invariably converted to strings in the process, which is generally undesirable (e.g., the Authentication property's numeric value of 0 would turn into string '0').

    Here's a more generic workaround based on a filter function that recursively introspects the input objects and outputs ordered hashtables that reflect the input properties with enumeration values converted to strings and all other values passed through, which you can then pass to ConvertTo-Json:

    Filter ConvertTo-EnumsAsStrings ([int] $Depth = 2, [int] $CurrDepth = 0) {
      if ($_ -is [enum]) { # enum value -> convert to symbolic name as string
        $_.ToString() 
      } elseif ($null -eq $_ -or $_.GetType().IsPrimitive -or $_ -is [string] -or $_ -is [decimal] -or $_ -is [datetime] -or $_ -is [datetimeoffset]) {
        $_
      } elseif ($_ -is [Collections.IEnumerable]) {
        , ($_ | ConvertTo-EnumsAsStrings -Depth $Depth -CurrDepth ($CurrDepth+1))
      } else { # non-primitive type -> recurse on properties
        if ($CurrDepth -gt $Depth) { # depth exceeded -> return .ToString() representation
          "$_"
        } else {
          $oht = [ordered] @{}
          foreach ($prop in $_.psobject.properties) {
            if ($prop.Value -is [Collections.IEnumerable] -and -not $prop.Value -is [string]) {
              $oht[$prop.Name] = @($prop.Value | ConvertTo-EnumsAsStrings -Depth $Depth -CurrDepth ($CurrDepth+1))
            } else {      
              $oht[$prop.Name] = $prop.Value | ConvertTo-EnumsAsStrings -Depth $Depth -CurrDepth ($CurrDepth+1)
            }
          }
          $oht
        }
      }
    }
    

    Caveat: As with ConvertTo-Json, the recursion depth (-Depth) is limited to 2 by default, to prevent infinite recursion / excessively large output (as you would get with types such as [System.IO.FileInfo] via Get-ChildItem, for instance). Similarly, values that exceed the implied or specified depth are represented by their .ToString() value. Use -Depth explicitly to control the recursion depth.

    Example call:

    PS> [pscustomobject] @{ p1 = [platformId]::Unix; p2 = 'hi'; p3 = 1; p4 = $true } | 
          ConvertTo-EnumsAsStrings -Depth 2 |
            ConvertTo-Json
    
    {
      "p1": "Unix",   # Enum value [platformId]::Unix represented as string.
      "p2": "hi",     # Other types of values were left as-is.
      "p3": 1,
      "p4": true
    }
    

    Note: -Depth 2 isn't necessary here, given that 2 is the default value (and given that the input has depth 0), but it is shown here as a reminder that you may want to control it explicitly.

    0 讨论(0)
提交回复
热议问题