Powershell - 使用ConvertTo-Json

时间:2017-06-12 08:09:42

标签: json powershell enums

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

Name                    Status   Authentication

myemail.onmicrosoft.com Verified Managed

当我使用" ConvertTo-Json"如下所示

GetMsolDomain |ConvertTo-Json

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

{
    "ExtensionData":  {

                      },
    "Authentication":  0,
    "Capabilities":  5,
    "IsDefault":  true,
    "IsInitial":  true,
    "Name":  "myemail.onmicrosoft.com",
    "RootDomain":  null,
    "Status":  1,
    "VerificationMethod":  1
}

然而,问题是,如果您在两个输出中都注意到状态属性,则它会有所不同。 VerificationMethod 属性也是如此。不使用ConvertTo-JSon Powershell给出Text,并使用ConvertTo-Json给出整数。

当我发出以下命令时

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

我得到输出为

{
    "Status":  "Verified"
}

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

Select-object @{Name='Status';Expression={"$($_.Status)"}}

此行仅转换状态属性,而不是 VerificationMethod 属性,因为这是我提供的输入。

问题:是否有一些通用的东西我可以给" ConvertTo-Json "命令行,以便它将 ALL 枚举属性作为文本而非整数返回,而不显式命名它们,以便我得到类似下面的内容作为输出:

{
    "ExtensionData":  {

                      },
    "Authentication":  0,
    "Capabilities":  5,
    "IsDefault":  true,
    "IsInitial":  true,
    "Name":  "myemail.onmicrosoft.com",
    "RootDomain":  null,
    "Status":  "Verified",
    "VerificationMethod":  "DnsRecord"
}

3 个答案:

答案 0 :(得分:6)

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

像这样:

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

答案 1 :(得分:2)

PowerShell Core 通过ConvertTo-Json -EnumsAsStrings开关提供了一种简单的解决方案。

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

不幸的是, Windows PowerShell 不支持该开关

Avshalom's answer提供了一个快速的解决方法,但需要注意以下事项:所有属性值在此过程中总是转换为字符串,这通常是不希望的。

这是基于过滤器功能的更通用的解决方法,它递归内省输入对象并输出有序哈希表,这些哈希表反映了输入属性,枚举值转换为字符串,并通过所有其他值,然后可以传递给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
    }
  }
}

注意事项:与ConvertTo-Json一样,默认情况下递归深度(-Depth)限制为2,以防止无限递归/输出过大(例如您通过[System.IO.FileInfo]使用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
}

注意:假设-Depth 2是默认值(并且输入的深度为2),此处0并不是必需的,但此处显示提醒您可能要显式控制它。

答案 2 :(得分:1)

我需要将 pwsh 对象序列化为 JSON,但无法使用 -EnumsAsStringsConvertTo-Json 参数,因为我的代码在 psv5 上运行。由于我在使用@mklement0 的代码时遇到了无限循环编者注:因为已修复。,我重新编写了它。我修改后的代码还处理了一些其他类型(例如日期)的序列化,将它们序列化为 ISO 8601 格式,这通常是在 JSON 中表示日期的公认方式。随意使用它,如果您遇到任何问题,请告诉我。

Filter ConvertTo-EnumsAsStrings ([int] $Depth = 10, [int] $CurrDepth = 0) {

  if ($CurrDepth -gt $Depth) {
    Write-Error "Recursion exceeded depth limit of $Depth"
    return $null
  }

  Switch ($_) {
    { $_ -is [enum] -or $_ -is [version] -or $_ -is [IPAddress] -or $_ -is [Guid] } {
      $_.ToString()
    }
    { $_ -is [datetimeoffset] } {
      $_.UtcDateTime.ToString('o')
    }
    { $_ -is [datetime] } {
      $_.ToUniversalTime().ToString('o')
    }
    { $_ -is [timespan] } {
      $_.TotalSeconds
    }
    { $null -eq $_ -or $_.GetType().IsPrimitive -or $_ -is [string] -or $_ -is [decimal] } {
      $_
    }
    { $_ -is [hashtable] } {
      $ht = [ordered]@{}
      $_.GetEnumerator() | ForEach-Object {
        $ht[$_.Key] = ($_.Value | ConvertTo-EnumsAsStrings -Depth $Depth -CurrDepth ($CurrDepth + 1))
      }
      if ($ht.Keys.Count) {
        $ht
      }
    }
    { $_ -is [pscustomobject] } {
      $ht = [ordered]@{}
      $_.PSObject.Properties | ForEach-Object {
        if ($_.MemberType -eq 'NoteProperty') {
          Switch ($_) {
            { $_.Value -is [array] -and $_.Value.Count -eq 0 } {
              $ht[$_.Name] = @()
            }
            { $_.Value -is [hashtable] -and $_.Value.Keys.Count -eq 0 } {
              $ht[$_.Name] = @{}
            }
            Default {
              $ht[$_.Name] = ($_.Value | ConvertTo-EnumsAsStrings -Depth $Depth -CurrDepth ($CurrDepth + 1))
            }
          }
        }
      }
      if ($ht.Keys.Count) {
        $ht
      }
    }
    Default {
      Write-Error "Type not supported: $($_.GetType().ToString())"
    }
  }
}