Powershell - 使用ConvertTo-Json保留所有Enum属性的文本

pun*_*eet 8 powershell enums json

对于" Get-Msoldomain "powershell命令 - 让我得到以下输出(让我们称之为输出#1),其中Name,Status和Authentication是属性名称,下面是它们各自的值.

Name                    Status   Authentication

myemail.onmicrosoft.com Verified Managed
Run Code Online (Sandbox Code Playgroud)

当我使用"ConvertTo-Json"命令时,如下所示

GetMsolDomain |ConvertTo-Json
Run Code Online (Sandbox Code Playgroud)

我以Json格式得到以下输出(让我们称之为输出#2).

{
    "ExtensionData":  {

                      },
    "Authentication":  0,
    "Capabilities":  5,
    "IsDefault":  true,
    "IsInitial":  true,
    "Name":  "myemail.onmicrosoft.com",
    "RootDomain":  null,
    "Status":  1,
    "VerificationMethod":  1
}
Run Code Online (Sandbox Code Playgroud)

但问题是,如果你注意到两个输出中的Status属性,那就不一样了.同样的情况对于VerificationMethod财产.不使用ConvertTo-JSon Powershell给出Text,并使用ConvertTo-Json给出整数.

当我给出下面的命令

get-msoldomain |Select-object @{Name='Status';Expression={"$($_.Status)"}}|ConvertTo-json
Run Code Online (Sandbox Code Playgroud)

我把输出作为

{
    "Status":  "Verified"
}
Run Code Online (Sandbox Code Playgroud)

但是,我想要一些东西,以便我不必指定任何特定的属性名称来进行转换,我在上面指定的方式是

Select-object @{Name='Status';Expression={"$($_.Status)"}}
Run Code Online (Sandbox Code Playgroud)

这一行只转换Status属性而不是VerificationMethod属性,因为这是我提供的输入.

问题:我是否可以为" ConvertTo-Json "命令行开关提供一些通用的东西,以便它将所有 Enum属性作为文本而不是整数返回,而不显式命名它们,以便我得到类似下面的输出:

{
    "ExtensionData":  {

                      },
    "Authentication":  0,
    "Capabilities":  5,
    "IsDefault":  true,
    "IsInitial":  true,
    "Name":  "myemail.onmicrosoft.com",
    "RootDomain":  null,
    "Status":  "Verified",
    "VerificationMethod":  "DnsRecord"
}
Run Code Online (Sandbox Code Playgroud)

mkl*_*nt0 8

PowerShell Core(PowerShell版本 6 及更高版本)通过ConvertTo-Json-EnumsAsStringsswitch提供了一个简单的解决方案。

GetMsolDomain | ConvertTo-Json -EnumsAsStrings  # PS *Core* (v6+) only
Run Code Online (Sandbox Code Playgroud)

不幸的是,Windows PowerShell不支持此开关。

Avshalom的答案提供然而,与一个重要的提醒,一个快速的解决方法:所有属性值都不约而同地转换为字符串的过程中,这通常是不希望(例如,Authentication属性的数值的数值0会变成字符串 '0')。

这是一个基于过滤器函数的更通用的解决方法,该函数递归地内省输入对象并输出反映输入属性的有序哈希表,枚举值转换为字符串和传递的所有其他值,然后您可以将其传递给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] -and $_ -isnot [Collections.IDictionary]) { # enumerable (other than a dictionary)
    , ($_ | ConvertTo-EnumsAsStrings -Depth $Depth -CurrDepth ($CurrDepth+1))
  } else { # non-primitive type or dictionary (hashtable) -> recurse on properties / entries
    if ($CurrDepth -gt $Depth) { # depth exceeded -> return .ToString() representation
      Write-Warning "Recursion depth $Depth exceeded - reverting to .ToString() representations."
      "$_"
    } else {
      $oht = [ordered] @{}
      foreach ($prop in $(if ($_ -is [Collections.IDictionary]) { $_.GetEnumerator() } else { $_.psobject.properties })) {
        if ($prop.Value -is [Collections.IEnumerable] -and $prop.Value -isnot [Collections.IDictionary] -and $prop.Value -isnot [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
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

警告:与 一样ConvertTo-Json,默认情况下递归深度 ( -Depth) 被限制为2,以防止无限递归/过大的输出(例如,您将使用诸如[System.IO.FileInfo]via 之类的类型Get-ChildItem)。同样,超过隐含或指定深度的.ToString()值由它们的值表示。-Depth显式使用来控制递归深度。

示例调用:

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
}
Run Code Online (Sandbox Code Playgroud)

注意:-Depth 2这里没有必要,因为这2是默认值(并且考虑到输入具有 depth 0),但是这里显示它是为了提醒您可能想要明确控制它。


如果要实现自定义表示额外的类型,例如[datetime][datetimoffset](使用ISO 8601兼容.NET往返时间串格式o,如PowerShell的(核心)V6 +自动做),以及[timespan][version][guid][ipaddress],请参阅Brett 对此答案的有用变体


Avs*_*lom 6

好吧,如果你不介意去旅行:)你可以将它转换为CSV,这将强制输出字符串,然后将其从CSV重新转换回PS对象,最后返回到Json.

像这样:

Get-MsolDomain | ConvertTo-Csv | ConvertFrom-Csv | ConvertTo-Json
Run Code Online (Sandbox Code Playgroud)
  • 如果您需要保留原始类型而不是将其全部转换为字符串,请参阅mklement0有用的答案...