Home > database >  Create XPath filtering on type value?
Create XPath filtering on type value?

Time:11-16

I have the below XML (that is part of a larger document):

 <class type="I want to filter on whatever value this is">
    <instance name="QuestionSet4">
      <property name="QuestionSetName">QuestionSet4</property>
      <property name="Ratio">5</property>
    </instance>
    <instance name="QuestionSetTrust">
      <property name="QuestionSetName">QuestionSetTrust</property>
      <property name="Ratio">5</property>
    </instance>
  </class>

My goal is to use PowerShell's Select-Xml such that I can return both of the name properties for each node using an XPath that filters on the class type, so that the output is something like this:

QuestionSetName, Ratio
QuestionSet4, 5
QuestionSetTrust, 5

The class type is unique in the document so I wondered if it was possible to filter based on that rather than hard coding the element like below:

/root/class[3]/@type

It would instead be /root/class/@type="I want to filter on whatever value this is"...

Instance names will change from document to document so I cannot filter on this either.

CodePudding user response:

You can resolve the relevant instance nodes with an XPath expression like the following:

//class[@type = 'I want to filter on whatever value this is']/instance

So you could construct the desired output objects like this:

$xml |Select-Xml -XPath "//class[@type = 'I want to filter on whatever value this is']/instance" |ForEach-Object { 
  [pscustomobject]@{
    QuestionSetName = ($_.Node |Select-Xml "./property[@name='QuestionSetName']").Node.InnerText
    Ratio           = ($_.Node |Select-Xml "./property[@name='Ratio']").Node.InnerText
  }
}

If you have a lot of <property /> elements to enumerate for every <instance /> it'll quickly get messy, but you can simplify it slightly like this:

$propertyNames = 'QuestionSetName','Ratio','Size','SomeOtherProperty'

$xml |Select-Xml -XPath "//class[@type = 'I want to filter on whatever value this is']/instance" |ForEach-Object { 
  $properties = [ordered]@{}
  foreach($name in $propertyNames){
    $properties[$name] = ($_.Node |Select-Xml "./property[@name='${name}']").Node.InnerText
  }

  [pscustomobject]$properties
}
  • Related