2

Given xml with a series of <Set> elements that should all have an id attribute, I need to validate that, and then process only those elements that do have an id. I had thought that

$elementIDs = $elements.GetAttribute('id')

would only return actual values, as in an element that either did not have the attribute or the attribute was blank would not be included in the array. Not so, I can get blank values back. Later, when I iterate through that list I can test for a blank id

foreach ($id in $elementIDs) {
    if ($id) {

But I would rather just have a clean list. I thought perhaps I could method chain, like so

$elementIDs = $elements.HasAttribute('id').GetAttribute('id')

but method chaining isn't an option here. So I thought .Where() might work. Either

$elementIDs = $elements.GetAttribute('id').Where({$_ -ne $Null})

or

$elementIDs = $elements.GetAttribute('id').Where({$args -ne $Null})

or even

$elementIDs = $elements.GetAttribute('id').Where({param ($id) $id -ne $Null})

But none seems to work. So, is there a simple, elegant way to do this, or am I stuck with $Nulls in my array and a conditional?

mklement0
  • 382,024
  • 64
  • 607
  • 775
Gordon
  • 6,257
  • 6
  • 36
  • 89

2 Answers2

2

Try something like this on your actual xml and see if it works:

$elements = $xml.SelectNodes('//Set[@id]');
$elementIDs = $elements.GetAttribute('id')
echo $elementIDs
Jack Fleeting
  • 24,385
  • 6
  • 23
  • 45
  • 1
    Aha, that gets the job done, just earlier in the process than I was thinking. I suspect this is even more efficient. But I do still wonder if there is a way do it in the second line, from a NodeList that contains both id attributes and not. Not needed for the current scenario, but if it CAN be done, I wouldn't mind knowing how, for the occasion when I really need that. – Gordon Aug 12 '22 at 20:04
  • @Gordon Not sure; let me think about it... – Jack Fleeting Aug 12 '22 at 20:34
1

To offer PowerShell-native alternatives to Jack Fleeting's helpful XPath-based answer:

Note that the .GetAttribute() method returns an empty string, not $null, if a given XML element doesn't have an attribute with the given name.

The ability of PowerShell's comparison operators to operate on arrays, in which case they act as filters, makes removing the empty-string return values easy:

@($elements.GetAttribute('id')) -ne ''

Assuming that none of your <Set> elements (also) have <id> child elements, an even simpler solution that uses PowerShell's adaptation of the XML DOM is possible:

$elements.id

Note:

  • Both attributes and child elements are surfaced as if they were regular properties.

  • Unlike when using member-access enumeration with true, type-native properties, where a $null is emitted for each object that doesn't have the specified property, with the XML adaptation nothing is emitted, which - conveniently - returns only actual id attributes / elements.

mklement0
  • 382,024
  • 64
  • 607
  • 775