The non-greedy duplication symbols (.*?
) are being honored, but they're not enough in this case:
<Partner>.*?<Name>$partyName</Name>
matches between <Partner>
and the next instance of the <Name>
element, but that doesn't guarantee that there won't be another <Partner>
tag in between.
In other words: Your regex will invariably match between the first <Partner>
tag and the <Name>
element of interest.
To prevent that, you need a negative look-ahead assertion ((?!...)
) that rules out intervening <Partner>
tags:
# Sample input, defined as a here-string.
$bindings = @'
starting stuff <Partner>
more stuff <Name>Test</Name>
other things </Partner> <Partner>
stuff of interest before <Name>CompanyX</Name>
stuff of interest after </Partner> even more </Partner> ending stuff
'@
# Escape the name to ensure it is treated as a literal inside the regex.
# Note: Not strictly necessary for sample value 'CompanyX'
$partyName = [regex]::Escape('CompanyX')
# Use a negative look-ahead assertion - (?!...) - to rule out intervening
# <Partner> tags before the <Name> element of interest.
if ($bindings -match "(?s)<Partner>((?!<Partner>).)*<Name>$partyName</Name>.*?</Partner>") {
# Output the match.
$matches[0]
} else {
Write-Warning 'No match.'
}
The above yields:
<Partner>
stuff of interest before <Name>CompanyX</Name>
stuff of interest after </Partner>
(?!<Partner>).
matches a single character (.
) not preceded by string <Partner>
.
This subexpression must itself be matched against each character (if any) between the opening <Partner>
and the <Name>
element of interest, hence it is wrapped in (...)*
I presume this makes for an inefficient matching algorithm, but it does work.
As mentioned, using proper XML parsing with an XPath query is worth considering as an alternative.
You could make this matching more efficient by using (?:...)*
as the wrapper, which tells the regex engine not to capture (the latest) match of the subexpression. ((...)
are capture groups, meaning that what the subexpression matches is reported as part of what automatic variable $Matches
returns, which is not needed here, so ?:
suppresses that).