Powershell показывает неправильный вывод для JSON-объекта и ADDRESS-свойства
В настоящее время я изо всех сил пытаюсь преобразовать строку JSON в массив объектов и ОБЩЕСТВЕННО обрабатывать свойства / атрибуты каждого объекта.
Вот простая демонстрация, показывающая, что, например, атрибут "адрес" кажется немного особенным:
cls
$json = '[{"id":"1","address":"1"},{"id":"2","address":"2"}]'
$list = $json | ConvertFrom-Json
$list.id # OK
$list.address # gives a weired result - is this a bug?
$list.GetEnumerator().address # that works
Это результат:
1
2
OverloadDefinitions
-------------------
System.Object&, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 Address(int )
1
2
Как видите, мне нужно добавить ".GetEnumerator()", чтобы получить правильные "адресные" значения.
Это ожидаемое? Должен ли я ВСЕГДА использовать ".GetEnumerator()" в целях безопасности?
1 ответ
Поскольку $list
представляет собой массив (коллекцию), используя что-то вроде$list.id
выполняет перечисление членов; это.id
свойство автоматически обращается к каждому элементу, и результирующие значения возвращаются в виде массива ([object[]]
)[1].
Однако, если массив / тип коллекции сам имеет элемент с таким именем (свойство или метод), он имеет преимущество, что и произошло в вашем случае: .NET массивы имеют.Address
метод (который добавляется средой выполнения - см. этот ответ), и это то, что вытесняет доступ к элементам .Address
свойство.
(То, что вы видели, было представлением PowerShell сигнатуры метода (перегрузки), которое вы получаете, когда получаете доступ к методу только по имени, без фактического вызова его путем добавления ((...)
); пытаться'foo'.ToUpper
, пример fo.)
PowerShell не предоставляет четкого синтаксиса оператора, позволяющего различать прямой доступ к членам и перечисление членов, является предметом этого обсуждения на GitHub, но существующее поведение вряд ли изменится.
Самый эффективный способ обойти эту проблему - использовать PSv4+. .ForEach()
array, который всегда нацелен на элементы коллекции:
$list = '[{"id":"1","address":"1"},{"id":"2","address":"2"}]' | ConvertFrom-Json
$list.ForEach('address')
Предупреждение: если вы используете Windows PowerShell и$list
может ситуативно привести к единственному pscustomobject
вместо массива вы должны заключить$list
в @(...)
, оператор подвыражения массива, чтобы гарантировать, что .ForEach()
метод доступен. Это[pscustomobject]
-специфическая ошибка (учитывая, что даже отдельные объекты должны постоянно иметь.ForEach()
метод), исправленный в PowerShell [Core] 6+.
# Necessary in Windows PowerShell only.
@($list).ForEach('address')
Примечание:
Технически это возвращает
[System.Collections.ObjectModel.Collection[PSObject]]
коллекцию, но в большинстве случаев вы можете использовать ее как обычный[object[]]
Массив PowerShell.В отличие от перечисления членов, экземпляр коллекции также возвращается, если во входной коллекции есть только один элемент (вместо того, чтобы возвращать значение свойства этого одного элемента как есть, как это происходит при перечислении членов).
В PSv3 вы можете использоватьForEach-Object
командлет илиSelect-Object -ExpandProperty
:
$list | ForEach-Object address # PSv2: | ForEach-Object { $_.address }
# OR
$list | Select-Object -ExpandProperty address
[1] Если в коллекции только один элемент, значение его свойства возвращается как есть, а не в виде (одноэлементного) массива, что является той же логикой, которая применяется при сборе выходных данных конвейера в переменной. То, что эта логика может быть неожиданной в контексте перечисления членов, которое является контекстом выражения, обсуждается в этом выпуске GitHub.