17

我正在使用 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's 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's 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 也会将其转换为对象数组。

如果我没有将 Get-XmlNamespaceManager 函数返回的值显式转换为 System.Xml.XmlNamespaceManager,则 .SelectSingleNode() 函数会引发以下错误,因为将错误的数据类型传递给函数的第二个参数。

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

因此,出于某种原因,PowerShell 没有维护返回变量的数据类型。我真的很想从一个函数中得到这个工作,这样我就不必到处复制粘贴这 3 行了。任何建议表示赞赏。谢谢。

4

2 回答 2

30

正在发生的事情是 PowerShell 正在将您的命名空间管理器对象转换为字符串数组。

我认为这与 PowerShell 在将对象发送到管道时“展开”集合的性质有关。我认为 PowerShell 将为任何实现 IEnumerable 的类型(具有 GetEnumerator 方法)执行此操作。

作为一种变通方法,您可以使用逗号技巧来防止这种行为并将对象作为整个集合发送。

function Get-XmlNamespaceManager([xml]$XmlDocument, [string]$NamespaceURI = "")
{
    ...
    $xmlNsManager.AddNamespace("ns", $NamespaceURI)
    return ,$xmlNsManager
}
于 2013-07-06T01:27:49.703 回答
1

更具体地说,这里发生的情况是您强键入 $fullyQualifiedModePath 的编码习惯正试图将 Get 的结果(它是对象列表)转换为字符串。

[字符串]$foo

将变量 $foo 限制为仅是一个字符串,无论返回什么。在这种情况下,您的类型约束是巧妙地搞砸了返回并使其成为 Object[]

此外,查看您的代码,我个人建议您使用 Select-Xml(内置于 V2 及更高版本),而不是进行大量手动编码的 XML 展开。您可以使用 -Namespace @{x="..."} 在 Select-Xml 中进行命名空间查询。

于 2013-07-06T18:54:56.440 回答