【问题标题】:PowerShell changes return object's typePowerShell 更改返回对象的类型
【发布时间】:2013-07-04 02:44:00
【问题描述】:

我正在使用 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 行了。任何建议表示赞赏。谢谢。

【问题讨论】:

  • 那么您对内置的Select-Xml cmdlet 有什么看法?使用命名空间就像将前缀哈希表传递给命名空间映射一样简单,例如$xml | Select-Xml -XPath '//dns:foo' -namespace @{dns='http://schema.foo.org'}
  • 感谢 Keith,我实际上没有听说过 Select-Xml cmdlet。我宁愿忽略 Xml 命名空间,因为我不关心它,但不幸的是 .SelectSingleNode() 需要它,正如我在我的博客文章 (blog.danskingdom.com/…) 中讨论的那样。使用 Select-Xml 会解决这个问题吗?
  • Select-Xml 只是在幕后调用SelectSingleNodeSelectNodes(来自XmlDocument),因此它仍然容易受到XML 命名空间的影响,这就是@Keith 使用-namespace 参数的原因。跨度>
  • 顺便说一句,有一种方法可以完全忽略 XML 命名空间,您只需要以不同的方式编写 Xpath。 This SO question 展示了如何。

标签: function powershell powershell-3.0 return-type powershell-ise


【解决方案1】:

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

[字符串]$foo

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

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

【讨论】:

  • $fullyQualifiedModePath 行不是他遇到的问题。就是这个[System.Xml.XmlNamespaceManager]$xmlNsManager =
【解决方案2】:

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

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

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

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

【讨论】:

  • 是的,XmlNamespaceManager 实现了 IEnumerable,因此 PowerShell 将其视为一系列项目。使用逗号运算符是解决此特定问题的方法,但在这种情况下确实不需要使用 return(函数的最后一行,因此无需提前返回)。
  • 感谢各位的解释。 Keith,我传统上是一名 C# 和 .Net 程序员,最近才接触 PowerShell,所以我仍然有使用 return 的习惯;我也喜欢它使函数返回的内容明确/明显。
  • @deadlydog 另一个问题(除了这个展开的事情)是任何返回未设置为变量或管道为 null 的值都将从您的函数返回,例如如果您调用 XmlDocument.AppendChild 方法,它将返回一个 XmlNode 对象,除非您阻止它,否则它将与您的命名空间管理器对象一起发送。
  • @AndyArismendi +1。这就是为什么我通常避免返回的原因,除非我需要在一个函数上提前保释。即使这样,我也倾向于不使用return <expr> 表单。它给人一种从函数返回的错误感觉。
  • @AndyArismendi 您还可以强制转换为 void 以抑制方法或 Cmdlet 的输出,即 [void] $xmlNsManager.AddNamespace("ns", $NamespaceURI)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-01-05
  • 2013-10-30
  • 1970-01-01
  • 1970-01-01
  • 2019-11-30
  • 1970-01-01
相关资源
最近更新 更多