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

beq87vna  于 2023-04-08  发布在  Shell
关注(0)|答案(1)|浏览(105)

请注意:

Windows PowerShell

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

Powershell内核

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

这是不好的。考虑一个键为整数的Map:

$x = @{
    123 = 1
}

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

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

这两个shell都是如此,行为上的变化破坏了我们使用REST API操作事物的自动化脚本。
Powershell Core的这种行为可以关闭吗?

cdmah0mi

cdmah0mi1#

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

  • Windows PowerShell 使用自定义实现,而PowerShell [Core] v6+ 从v7.1开始在幕后使用Json.NET library;请参阅this answer,了解该库默认情况下反序列化为System.Int64[long])的基本原理。
  • [**更新 :目前还不清楚这是否会发生-迁移到System.Text.Json将取消使用 * 单引号属性名称和字符串值的能力,Windows PowerShell和PowerShell(核心)目前都支持。

从PowerShell 7.1开始,有一个计划[?]移动到现在的原生.NET JSON功能(在.NET Core 3+中可用),可通过System.Text.Json namespace获得,这*可能 * 在默认情况下恢复序列化到System.Int32[int],考虑到破坏性的更改无论如何都是不可避免的:

  • 请参阅GitHub issue #14264(由iRon基于此问题创建)和GitHub PR #11198 [* 现已关闭 *]中的讨论,该讨论正在准备迁移到System.Text.Json
  • 一个相关的问题是太大而无法放入System.Int64[long])的数字也以不同的方式序列化(参见GitHub issue #9207):
  • Windows PowerShell:首先选择System.Decimal[decimal]),对于更大的数字System.Double[double])。
  • PowerShell [Core]自v7.1起:总是选择System.BigInt[bigint])。
  • 此外,在支持的格式*数量方面也存在**差异:
  • 根据JSON spec [1],Windows PowerShell * 不 * 识别 * 十六进制 * 数字(例如,0x10),而PowerShell [Core]从v7.1 * 开始 *;然而,作为规范的另一个扩展,两者都支持 * 科学记数法 *(例如,1001e2)并将其解析为[double]
  • Windows PowerShell,作为规范的另一个扩展,does 支持+-前缀数字(例如,+10),而PowerShell [Core]从v7.1开始不支持 *。
  • (此外,两个版本都支持 * 单 *-引号字符串作为扩展。
    解决方法
  • 一般来说,请注意,由于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 隐式地成为property names,它总是 * string *。

# 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
  • 警告 *:当一个键被隐式字符串化为一个属性名时,它总是使用一个 decimal 表示;例如,[pscustomobject] @{ [int] 0x10 = 'bingo' }会导致一个属性名为'16'的对象。

但是,请注意,哈希表/字典比[pscustomobject]更轻量级。
[1]然而,JSON5(旨在改进JSON)确实支持hex. numbers,沿着其他值得注意的改进,例如支持注解,额外的尾随逗号和 * 单 * 引用字符串。
[2]此外,对于[double]值,转换是 * 文化敏感的 *,因此1.2在某些文化中可能会导致'1.2'(从v7.1开始,这是意想不到的-请参阅GitHub issue #14278);此外,大的[double] s可能以 * 科学 * 表示法结束,因此1000000000000000.1的结果是'1E+15'。也就是说,考虑到从十进制数转换的准确性限制,使用[double] s作为字典键通常是不明智的。

相关问题