2014-09-19 115 views
2

出于某种原因,如果我的XML文件只包含一个计算机节点,但它返回一个列表(这是我总是期望的),如果有的话,下面的Read-TestControllerXml powershell函数返回一个System.Xml.XmlElement类型的对象多个计算机节点。为什么这个Powershell函数有时会返回一个列表,而其他时间会返回一个XmlElement?

的XML文件的示例如下:

<test_controller> 
    <defaults> 
     <nunit path='C:\Program Files (x86)\NUnit 2.6.2\bin\nunit-console.exe' /> 
     <nunit_results local_path='C:\Test_Results' remote_path='C:\Test_Results' /> 
     <powershell path='C:\windows\system32\WindowsPowerShell\v1.0\powershell.exe' /> 
     <psexec path='C:\PSTools\PsExec.exe' /> 
    </defaults> 

    <platform os='windows' version='2012' cpu='x64'> 
     <computer hostname='VM-2012R2-03' alias='standalone-01' configuration='standalone' image='VM-2012R2-03:Clean' /> 
    </platform> 
</test_controller> 

这里是PowerShell的功能:

#################################################################### 
# Parses the TestController.xml file and returns a list of Computer XML objects. 
# 
# Syntax: 
#  Read-TestControllerXml [-TestControllerXml] <path to TestController.xml> [<CommonParameters>] 
# 
# Returns: An array of [Xml] nodes for each of the <computer> tags in TestController.xml. 
#################################################################### 
function Read-TestControllerXml 
{ 
    [CmdletBinding()] 
    Param 
    (
     [parameter(Mandatory=$true)] 
     [string] $TestControllerXml 
    ) 

    $ErrorActionPreference = "Stop" 

    # Read the TestController XML configuration file 
    $TestController = [xml] (Get-Content $TestControllerXml) 

    Write-Verbose "Selecting computer nodes..." 
    $computerNodes = $TestController.SelectNodes("//test_controller/platform/computer") 
    Write-Verbose "Selecting default nodes..." 
    $defaultNodes = $TestController.SelectSingleNode("//test_controller/defaults") 

    $computers = @() 

    foreach ($computerNode in $computerNodes) 
    { 
     Write-Verbose "***** Before adding default attributes *****" 
     Write-Verbose "$($computerNode.OuterXml)" 

     # Add default attributes to the node 
     foreach ($dnode in $defaultNodes.ChildNodes) 
     { 
      if ($dnode.NodeType -eq "Comment") { continue } # Skip comments. 

      # Search for the node items in the defaults section 
      $cnode = $computerNode 

      if ($dnode.Name -ne $computerNode.Name) 
      { 
       Write-Verbose "Selecting '$($dnode.Name)' nodes..." 
       $cnode = $computerNode.SelectSingleNode($dnode.Name) 
      } 

      # Append the default nodes that do not exist in the computer specific sections 
      if ($cnode -eq $null) 
      { 
       Write-Verbose "*** cnode != null cnode.OuterXml is: '$($cnode.OuterXml)'" 
       Write-Verbose "Appending default '$($dnode.Name)' node that don't exist in the computer nodes..." 
       $cnode=$computerNode.AppendChild($TestController.ImportNode($dnode, $true)) 
      } 
      # Append the default attributes that do not exist in the computer specific sections 
      else 
      { 
       Write-Verbose "Appending default attributes that don't exist in computer nodes..." 
       foreach ($attribute in $dnode.Attributes) 
       { 
        if (-not $cnode.HasAttribute($attribute.Name)) 
        { 
         Write-Verbose "Adding attribute '$($attribute.Name)=$($attribute.Value)' to the computer node." 
         $cnode.SetAttribute($attribute.Name, $attribute.Value) 
        } 
       } 
      } 
     } 

     Write-Verbose "***** After adding default attributes *****" 
     Write-Verbose "$($computerNode.OuterXml)" 
     Write-Verbose "************************************************************" 

     $computers += $computerNode 
    } 

    return $computers 
} 

回答

0

我刚刚发现这个类似的问题和解决办法从那里工作原理: Function not returning expected object

即改变:

return $computers 

到:

return ,$computers 
+0

有没有理由讨厌powershell。它试图用一个元素来帮助你。你的数据总是好的! – Matt 2014-09-19 23:25:49

+0

您必须记住,PowerShell是一种'Shell脚本语言',主要针对系统管理员。管理员不必担心它们的函数是否返回0,1或N结果,以便它在管道中工作。也就是说,当一个标量值放在管道的前端时,管道预计会处理该单个对象。当代表一个集合的值被放在一个管道的前端时,管道预期会处理集合中的每个单独对象而不是集合作为单个对象。 – 2014-09-20 04:03:41

+0

逗号技巧只是将数组包装到另一个数组中,以便通过展开数组输出的单个对象是原始数组。注意:一般来说这不建议用于正确的流水线操作,例如如果我想使用你的修改函数,我不得不这样做'Read-TestControllerXml $ xmlPath | Foreach {$ _} | Foreach {..用XmlElement做一些事情......}'而不是像这样直接做'Read-TestControllerXml $ xmlPath | Foreach {..用XmlElement做一些事情..} – 2014-09-20 04:07:13

1

我把你的榜样作用和样本数据。运行函数我得到你描述的输出。

PS C:\Users\Matt> (Read-TestControllerXml C:\temp\xml.txt).GetType().FullName 
System.Xml.XmlElement 

然后,我将计算机节点添加到示例文件并再次运行该函数。

PS C:\Users\Matt> (Read-TestControllerXml C:\temp\xml.txt).GetType().FullName 
System.Object[] 

这是正确的,因为你的函数返回和对象数组。如果你分解这些项目并检查它们的类型,那应该是合理的。

PS C:\Users\Matt> Read-TestControllerXml C:\temp\xml.txt | ForEach-Object{$_.GetType().FullName} 
System.Xml.XmlElement 
System.Xml.XmlElement 

我有两个项目,并把它们放到一个foreach - 对象循环和检查那里单独类型。由于你的输出是一个数组,期望有多个对象。总之,你的函数总是返回[System.Xml.XmlElement]只是当有多个它是一个System.Object[]数组。

对于好奇心起见

我试图强迫$计算机是XML元素这样$computers = [System.Xml.XmlElement[]]@()的阵列。它没有改变输出。多项目,仍输出作为System.Object[]

相关问题