简体   繁体   中英

XML Parsing using Powershell

I am trying to parse an xml file using powershell. I want to print each node and its subnodes. I am new to xml parsing.

<?xml version="1.0" encoding="UTF-8"?>
<Inventory>
  <Roles>
    <Role Name="VirtualMachinePowerUser" Label="Virtual machine power user (sample)" Summary="Provides virtual machine interaction and configuration permissions">
      <Privilege Name="Datastore.Browse" />
      <Privilege Name="Global.CancelTask" />
      <Privilege Name="ScheduledTask.Create" />
</Role>
    <Role Name="VirtualMachineUser" Label="Virtual machine user (sample)" Summary="Provides virtual machine interaction permissions">
      <Privilege Name="Global.CancelTask" />
      <Privilege Name="ScheduledTask.Create" />
</Role>

My code below

[xml]$inputFile = Get-Content "C:\RolesnPer.xml"     
$nodelist = $inputFile.Inventory.Roles.Role |Select-Object -Property Name
foreach ($Role in $nodelist)
{
    $Role
    $XMLprinterPath = $Role.selectSingleNode("Privilege").get_innerXml()
}

Required output:

Name                      Privilege
VirtualMachinePowerUser   Datastore.Browse
                          Global.CancelTask
                          ScheduledTask.Create
VirtualMachineUser        Global.CancelTask
                          ScheduledTask.Create

But I am getting this output:

Name
----
VirtualMachinePowerUser
Method invocation failed because [Selected.System.Xml.XmlElement] does not contain a method named 'selectSingleNode'.
At C:\Mandy\Code\transformation.ps1:25 char:1
+ $XMLprinterPath = $Role.selectSingleNode("Privilege").get_innerXml()
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidOperation: (selectSingleNode:String) [], RuntimeException
    + FullyQualifiedErrorId : MethodNotFound

VirtualMachineUser
Method invocation failed because [Selected.System.Xml.XmlElement] does not contain a method named 'selectSingleNode'.
At C:\Mandy\Code\transformation.ps1:25 char:1
+ $XMLprinterPath = $Role.selectSingleNode("Privilege").get_innerXml()
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidOperation: (selectSingleNode:String) [], RuntimeException
    + FullyQualifiedErrorId : MethodNotFound

Deadrobot's helpful answer diagnoses your problem correctly ( Select-Object emits [pscustomobject] instances, not XML nodes), but there's a simpler solution:

Select-Xml -LiteralPath C:\RolesnPer.xml '//Role' | ForEach-Object {
  [pscustomobject] @{
    Name = $_.Node.Name
    Privilege = $_.Node.Privilege.Name
  }
}

The above yields:

Name                    Privilege
----                    ---------
VirtualMachinePowerUser {Datastore.Browse, Global.CancelTask, ScheduledTask.Create}
VirtualMachineUser      {Global.CancelTask, ScheduledTask.Create}

Explanation:

  • The Select-Xml cmdlet can operate on files directly for extracting matching nodes with XPath queries.

  • Each matching node can be accessed via $_.Node inside the ForEach-Object script block, and PowerShell conveniently exposes the child elements and attributes of XML element nodes as direct properties, so that .Name reports the value of the Name attribute, and .Privilege returns all Privilege child elements as an array, and, thanks to member enumeration , accessing their .Name property returns an array of all their Name attributes.

  • [pscustomobject] @{ ... } is PSv3+ syntactic sugar for constructing a custom object using hashtable syntax.

Note: While the output format is not exactly what you asked for , outputting objects (rather than using string formatting) gives you more flexibility for subsequent programmatic processing .


If you do need the exact output format specified in your question :

# The format string to use with the -f operator below.
# The first column is 30 chars. wide; adjust as needed.
$fmtStr = '{0,-30} {1}'

# Output the header line.
$fmtStr -f 'Name', 'Privilege'

# Query the XML document and output the data lines.
Select-Xml -LiteralPath t.xml '//Role' | ForEach-Object {
  $name = $_.Node.Name
  foreach ($priv in $_.Node.Privilege.Name) {
    $fmtStr -f $name, $priv
    $name = ''
  }
}

This is occurs because Select-Object on line 2 creates a new object that isn't xml.

I'm not an expert on manipulation XML, so there probably is a better way, but I would code it as such to get the output you want:

[xml]$inputFile = Get-Content "C:\RolesnPer.xml"  
$Nodelist = $inputFile.SelectNodes("//Role")
$ParsedOutput = @()
foreach ($Role in $Nodelist) {
    $Name = $Role.Name
    $Privilege = $Role.Privilege |Select-Object -ExpandProperty name
    $Obj = New-Object -TypeName psobject
    $Obj | Add-Member -MemberType NoteProperty -Name Name -Value $Name
    $Obj | Add-Member -MemberType NoteProperty -Name Privilege -Value $Privilege
    $ParsedOutput += $Obj
}

$ParsedOutput

I know that answers have already been provided on accessing the element data, but this post is mainly to address the output formatting request:

$inputFile = [xml](Get-Content "C:\RolesnPer.xml")
$nodes = $inputFile.Inventory.Roles.Role | Select-Object -Property Name,Privilege
$NameColWidth = ($nodes.name | Foreach-Object { $_.length } | Sort-Object -Desc)[0] + 2
$FormattedOutput = @("{0,-$NameColWidth}{1}" -f "Name","Privilege") -as [collections.arraylist]
foreach ($node in $nodes) {
    $null = $FormattedOutput.Add(("{0,-$NameColWidth}{1}" -f $node.name,($node.privilege.name | Select-Object -First 1)))
    $node.Privilege | Select -expand Name -skip 1 | Foreach-Object {
        $null = $FormattedOutput.Add(("{0,-$NameColWidth}{1}" -f " ",$_))
    }
}
$FormattedOutput

If you only want the PowerShell object array with the two properties (Name and Privilege) without formatting, you only need the first two lines. Then output the variable $nodes .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM