Powershell Core десериализует числа в JSON как Int64 по сравнению с Windows Powershell, который делает это как 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, он будет иметь разные типы в разных оболочках. В настоящее время:

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

И это верно для обеих оболочек. Это изменение в поведении разрушает наши сценарии автоматизации, которые манипулируют вещами с помощью REST API.

Можно ли отключить такое поведение Powershell Core?


person mark    schedule 20.11.2020    source источник
comment
Почему бы вам не привести ключ к нужному типу?   -  person Zan Lynx    schedule 20.11.2020
comment
@Zan Lynx, для одного ключа это легко, для сотен ... (образцы и реальная жизнь не всегда совпадают;)). @iRon, попробуйте это на PowerShell 7 $x=@{One=1}, затем $x.One.GetType() и затем ($x|ConvertTo-Json|ConvertFrom-Json).One.GetType(). Это действительно актуальная проблема, я не думаю, что есть способ отключить это. Может быть предложение для коммита в следующем выпуске с параметром -Legacy или -Int32 для ConvertFrom-Json   -  person CFou    schedule 20.11.2020
comment
Поведение ConvertFrom-Json, измененное в PowerShell [Core] v6+, немного изменилось, я думаю, это связано с описанием в этом выпуске: #9207 convertto-json bigint неправильно сериализован   -  person iRon    schedule 20.11.2020
comment
Почему мы вообще говорим о ConvertTo-Json?   -  person mark    schedule 20.11.2020
comment
Какая связь между этими двумя явлениями? Вы анализируете значения листьев json и используете их в качестве ключей динамического словаря, не преобразовывая их сначала в строки?   -  person Mathias R. Jessen    schedule 20.11.2020
comment
Я обрабатываю результаты REST Api любым подходящим способом, который может включать использование значений, возвращаемых REST Api, для поиска записей в хеш-таблице. И эти значения могут быть числами, и нет, я не преобразовываю их в строки, потому что мне никогда не приходилось.   -  person mark    schedule 20.11.2020
comment
Вы правы: $a = "1" | ConvertFrom-Json; (@{ $a = 2 }).1 возвращается в 2 в Windows PowerShell 5 и ничего в PowerShell Core 7.1.   -  person iRon    schedule 20.11.2020
comment
См. также: Сделать Newtonsoft JsonConvert по умолчанию Int32, а не Int64   -  person iRon    schedule 20.11.2020
comment
К вашему сведению: я сообщил об этой проблеме здесь: Powershell Core десериализует числа в JSON как Int64 по сравнению с Windows Powershell, который это как Int32 #14264   -  person iRon    schedule 25.11.2020
comment
@iRon - спасибо за проблему с github.   -  person mark    schedule 09.12.2020


Ответы (1)


  • В двух выпусках PowerShell используются разные реализации, что приводит к наблюдаемому вами различному поведению:

    • Windows PowerShell uses a custom implementation, whereas PowerShell [Core] v6+, as of v7.1, uses the Json.NET library behind the scenes; see this answer for that library's rationale for deserializing to System.Int64 ([long]) by default.
  • Начиная с 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: first chooses System.Decimal ([decimal]) and for even larger numbers System.Double ([double]).
      • PowerShell [Core] начиная с версии 7.1: всегда выбирает System.BigInt ([bigint]).
    • Кроме того, существуют различия в поддерживаемых числовых форматах:

      • Windows PowerShell не распознает шестнадцатеричные числа (например, 0x10) в соответствии с Спецификация JSON[1], тогда как PowerShell [Core] версии 7.1 делает; однако, как еще одно расширение спецификации, обе поддерживают научную запись (например, 1e2 для 100) и анализируют ее как [double].

      • Windows PowerShell, как еще одно расширение спецификации, поддерживает числа с префиксом + (например, +10), тогда как PowerShell [Core] версии 7.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] вместо хеш-таблицы, и в этом случае числовые ключи неявно становятся именами свойств, которые всегда строки.

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

Это будет работать даже с числовой переменной:

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

Предупреждение: когда ключ неявно преобразуется в строку, чтобы стать именем свойства, всегда используется десятичное представление; например, [pscustomobject] @{ [int] 0x10 = 'bingo' } приводит к объекту с именем свойства '16'.[2]

Однако обратите внимание, что хэш-таблицы/словари более легкие, чем [pscustomobject]s.


[1] Однако формат JSON5, предназначенный для улучшения JSON, поддерживает шестнадцатеричный формат. числа, а также другие заметные улучшения, такие как поддержка комментариев, посторонние запятые в конце и одинарные строки в кавычках.

[2] Кроме того, при использовании значений [double] преобразование чувствительно к культуре, так что 1.2 может в некоторых культурах привести к '1.2' (начиная с версии 7.1, что неожиданно — см. GitHub issue #14278); кроме того, большие [double] могут заканчиваться в научной нотации, так что 1000000000000000.1 приводит к '1E+15'. Тем не менее, использование [double]s в качестве ключей словаря, как правило, нецелесообразно, учитывая пределы точности его преобразования из десятичного числа.

person mklement0    schedule 27.11.2020
comment
Также отмечено в github.com/json-schema. -org/understanding-json-schema/issues/ - person lit; 27.11.2020