PowerShell меняет тип возвращаемого объекта

Я использую PowerShell v3 и Windows PowerShell ISE. У меня есть следующая функция, которая отлично работает:

function Get-XmlNode([xml]$XmlDocument, [string]$NodePath, [string]$NamespaceURI = "", [string]$NodeSeparatorCharacter = '.')
{
    # If a Namespace URI was not given, use the Xml document default namespace.
    if ([string]::IsNullOrEmpty($NamespaceURI)) { $NamespaceURI = $XmlDocument.DocumentElement.NamespaceURI }   

    # In order for SelectSingleNode() to actually work, we need to use the fully qualified node path along with an Xml Namespace Manager, so set them up.
    [System.Xml.XmlNamespaceManager]$xmlNsManager = New-Object System.Xml.XmlNamespaceManager($XmlDocument.NameTable)
    $xmlNsManager.AddNamespace("ns", $NamespaceURI)

    [string]$fullyQualifiedNodePath = Get-FullyQualifiedXmlNodePath -NodePath $NodePath -NodeSeparatorCharacter $NodeSeparatorCharacter

    # Try and get the node, then return it. Returns $null if the node was not found.
    $node = $XmlDocument.SelectSingleNode($fullyQualifiedNodePath, $xmlNsManager)
    return $node
}

Теперь я создам несколько подобных функций, поэтому я хочу разбить первые 3 строки на новую функцию, чтобы мне не пришлось копировать их всюду, поэтому я сделал это:

function Get-XmlNamespaceManager([xml]$XmlDocument, [string]$NamespaceURI = "")
{
    # If a Namespace URI was not given, use the Xml document default namespace.
    if ([string]::IsNullOrEmpty($NamespaceURI)) { $NamespaceURI = $XmlDocument.DocumentElement.NamespaceURI }   

    # In order for SelectSingleNode() to actually work, we need to use the fully qualified node path along with an Xml Namespace Manager, so set them up.
    [System.Xml.XmlNamespaceManager]$xmlNsManager = New-Object System.Xml.XmlNamespaceManager($XmlDocument.NameTable)
    $xmlNsManager.AddNamespace("ns", $NamespaceURI)
    return $xmlNsManager
}

function Get-XmlNode([xml]$XmlDocument, [string]$NodePath, [string]$NamespaceURI = "", [string]$NodeSeparatorCharacter = '.')
{
    [System.Xml.XmlNamespaceManager]$xmlNsManager = Get-XmlNamespaceManager -XmlDocument $XmlDocument -NamespaceURI $NamespaceURI
    [string]$fullyQualifiedNodePath = Get-FullyQualifiedXmlNodePath -NodePath $NodePath -NodeSeparatorCharacter $NodeSeparatorCharacter

    # Try and get the node, then return it. Returns $null if the node was not found.
    $node = $XmlDocument.SelectSingleNode($fullyQualifiedNodePath, $xmlNsManager)
    return $node
}

Проблема заключается в том, что при выполнении "return $xmlNsManager" возникает следующая ошибка:

Cannot convert the "System.Object[]" value of type "System.Object[]" to type "System.Xml.XmlNamespaceManager".

Итак, хотя я явно передал переменные $xmlNsManager типа System.Xml.XmlNamespaceManager, когда он возвращается из функции Get-XmlNamespaceManager, PowerShell преобразует его в массив Object.

Если я явно не передал значение, возвращаемое из функции Get-XmlNamespaceManager, в System.Xml.XmlNamespaceManager, тогда из функции .SelectSingleNode() выдается следующая ошибка, поскольку в функцию передается неправильный тип данных 2-й параметр.

Cannot find an overload for "SelectSingleNode" and the argument count: "2".

Поэтому по какой-то причине PowerShell не поддерживает тип данных возвращаемой переменной. Я бы очень хотел, чтобы эта работа работала от функции, так что мне не нужно копировать эти 3 строки по всему месту. Любые предложения приветствуются. Спасибо.

Ответы

Ответ 1

Что происходит, PowerShell преобразует ваш объект управления пространством имен в массив строк.

Я думаю, что это связано с природой PowerShell "разворачивания" коллекций при отправке объектов по конвейеру. Я думаю, что PowerShell сделает это для любого типа, реализующего IEnumerable (имеет метод GetEnumerator).

В качестве работы вы можете использовать трюк с запятой, чтобы предотвратить это поведение и отправить объект как целую коллекцию.

function Get-XmlNamespaceManager([xml]$XmlDocument, [string]$NamespaceURI = "")
{
    ...
    $xmlNsManager.AddNamespace("ns", $NamespaceURI)
    return ,$xmlNsManager
}

Ответ 2

Более конкретно, что здесь происходит, так это то, что ваша привычка кодирования для строгого ввода $fullQualifiedModePath пытается превратить результат Get (который представляет собой список объектов) в строку.

[string] $foo

будет ограничивать переменную $foo только строкой, независимо от того, что вернулось. В этом случае ваше ограничение типа - это то, что тонко прикручивает возврат и делает его Object []

Кроме того, глядя на ваш код, я лично рекомендую вам использовать Select-Xml (встроенный в V2 и более поздние версии), а не делать много ручного разметки XML. Вы можете делать запросы пространства имен в Select-Xml с -Namespace @{x = "..." }.