Powershell Core 将 JSON 中的数字反序列化为 Int64,而 Windows Powershell 将其反序列化为 Int32

Powershell Core deserializes numbers in JSON as Int64 vs Windows Powershell which does it as Int32

请注意:

Windows Powershell

C:\> ("1" | ConvertFrom-Json).gettype().name
Int32
C:\>

Powershell 核心

C:\> ("1" | ConvertFrom-Json).gettype().name
Int64
C:\>

这不是良性的。考虑一个键为整数的映射:

$x = @{
    123 = 1
}

键 123 是 Int32,而不是 Int64。因此,如果 123 来自已解析的 JSON ,那么在不同的 shell 中它将具有不同的类型。现在:

C:\> $x[[Int32]123]
1
C:\> $x[[Int64]123]
C:\>

这在两个 shell 上都是如此。这种行为变化破坏了我们使用 REST API 操作事物的自动化脚本。

可以关闭 Powershell Core 的这种行为吗?

  • 两个 PowerShell 版本使用不同的实现,导致您观察到的不同行为:

    • Windows PowerShell 使用自定义实现,而 PowerShell [Core] v6+,截至v7.1,默认使用 Json.NET library behind the scenes; see 作为该库反序列化为 System.Int64 ([long]) 的基本原理。
  • 从 PowerShell 7.1 开始,计划迁移到现在的原生 .NET JSON 功能(在 .NET Core 3+ 中可用)通过 System.Text.Json namespace 可用, 可以 将序列化恢复为 System.Int32 ([int]) 默认情况下 ,给定无论如何,重大变化是不可避免的:

    • 请参阅 GitHub issue #14264 (created by iRon based on this question) and GitHub PR #11198 中的讨论,该讨论正在准备迁移到 System.Text.Json

    • 一个相关的问题是 太大而无法放入 System.Int64 ([long]) 的序列化方式也不同 (请参阅GitHub issue #9207):

      • Windows PowerShell:首先选择 System.Decimal ([decimal]),对于更大的数字 System.Double ([double]).
      • 从 v7.1 开始的 PowerShell [Core]:始终选择 System.BigInt ([bigint])。
    • 此外,支持的格式方面存在差异:

      • Windows PowerShell 识别十六进制 数字(例如,0x10) ,根据 JSON spec[1],而从 v7.1 开始的 PowerShell [Core] 确实 ;然而,作为规范的另一个扩展,两者都支持 科学计数法 (例如 1e2 表示 100)并将其解析为 [double].

      • Windows PowerShell,作为规范的另一个扩展,确实支持+-前缀数字(例如,+10),而从 v7.1 开始的 PowerShell [Core]

      • (此外,两个版本都支持 引号字符串作为扩展。)


解决方法

  • 一般来说,请注意问题通常不会浮出水面,因为 PowerShell 能够混合不同的数字类型并根据需要扩展类型。

  • 但是,如问题所示,当数字用作哈希表(字典)的键时,类型精确值必须 传递以供查找以查找条目。

因此,最简单的解决方法是 哈希表键 转换为 [int],这允许以后仅使用,比如,[123](甚至 .123):

# Works in both Windows PowerShell and PowerShell [Core]
# Without the [int] cast, the lookup would fail in PowerShell [Core] as of v7.1
PS> $key = '123' | ConvertFrom-Json; @{ [int] $key = 'bingo' }[123]
bingo

另一种选择是使用 [pscustomobject] 而不是哈希表,在这种情况下,数字 keys 隐式成为 属性 names,它们总是 strings.

# Note the use of .123, i.e. property access.
PS> $key = '123' | ConvertFrom-Json; ([pscustomobject] @{ [int] $key = 'bingo' }).123
bingo

即使使用 numeric 变量也可以:

$lookup=123; ([pscustomobject] @{ [int] $key = 'bingo' }).$lookup

警告:当键被隐式字符串化为属性名称时,它总是十进制表示用来;例如,[pscustomobject] @{ [int] 0x10 = 'bingo' } 会生成一个 属性 名称为 '16' 的对象。[2]

但是,请注意哈希表/字典比 [pscustomobject] 更轻量级。


[1] 但是,旨在改进 JSON 的 JSON5 格式确实支持十六进制。数字,以及其他显着的改进,例如对注释的支持、多余的尾随逗号和 引号字符串。

[2] 此外,对于 [double] 值,转换是 文化敏感的 ,因此 1.2 可以在某些文化中导致 '1.2'(从 v7.1 开始,这是意外的 - 参见 GitHub issue #14278);此外,大的 [double] 可以以 科学 表示法结束,因此 1000000000000000.1 会导致 '1E+15'。也就是说,考虑到从十进制数转换的精度限制,使用 [double]s 作为字典键通常是不明智的。