web-dev-qa-db-ja.com

PowerShellの変更により、オブジェクトのタイプが返されます

PowerShellv3とWindowsPowerShellISEを使用しています。私はうまく機能する次の関数を持っています:

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に明示的にキャストしないと、関数の2番目のパラメーターに間違ったデータ型が渡されるため、.SelectSingleNode()関数から次のエラーがスローされます。

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

そのため、何らかの理由で、PowerShellは戻り変数のデータ型を維持していません。これらの3行をコピーして貼り付ける必要がないように、これを関数から機能させたいと思っています。任意の提案をいただければ幸いです。ありがとう。

17
deadlydog

何が起こっているのかというと、PowerShellは名前空間マネージャーオブジェクトを文字列配列に変換しています。

これは、オブジェクトをパイプラインに送信するときにコレクションを「展開」するPowerShellの性質に関係していると思います。 PowerShellは、IEnumerable(GetEnumeratorメソッドを持つ)を実装するすべてのタイプに対してこれを実行すると思います。

回避策として、コンマトリックを使用してこの動作を防ぎ、オブジェクトをコレクション全体として送信できます。

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

より具体的には、ここで起こっていることは、$ fullyQualifiedModePathを強く入力するコーディング習慣がGetの結果(オブジェクトのリスト)を文字列に変換しようとしていることです。

[文字列] $ foo

何が戻ってきても、変数$ fooを文字列のみに制約します。この場合、型の制約は、リターンを微妙に台無しにしてObject []にしているものです。

また、コードを見ると、手作業でコーディングされたXMLを大量に展開するのではなく、Select-Xml(V2以降に組み込まれている)を使用することを個人的にお勧めします。 Select-Xmlで-Namespace @ {x = "..."}を使用して名前空間クエリを実行できます。

1