Search code examples
xmlpowershellxpathpowershell-core

How to call native XPath functions from Select-Xml?


Using PowerShell Core 7.4.6.

Given the file /path/to/file.csproj

<Project Sdk="Microsoft.NET.Sdk">
    <ItemGroup>
        <ProjectReference Include="Path\To\Something.csproj" />
    </ItemGroup>
</Project>

I expect that the following snippet will select the ProjectReference node from that file, using the built-in XPath ends-with function

select-xml -path "/path/to/file.csproj" `
    -xpath "/Project/ItemGroup/ProjectReference[ends-with(@Include, 'Something.csproj')]"

but instead it yields the error

Select-Xml: Namespace Manager or XsltContext needed. This query has a prefix, variable, or user-defined function.

If I try to explicitly scope it using the XPath function namespace

select-xml -path "/path/to/file.csproj" `
    -xpath "/Project/ItemGroup/ProjectReference[fn:ends-with(@Include, 'Something.csproj')]" `
    -namespace @{ "fn" = "http://www.w3.org/2005/xpath-functions" }

I get a different error

Select-Xml: XsltContext is needed for this query because of an unknown function.

What am I missing? The documentation for Select-Xml mentions no limitations on invoking XPath functions, so I assume they are natively supported.


Solution

  • The built-in XPath support in .NET only covers functions specified by XPath version 1.0/1.1 - and ends-with was not part of XPath until version 2.

    You can use substring/string-length to cut the tail of the string and look for that instead:

    $tailValue = 'Something.csproject'
    $xPathEndsWithExpression = "/Project/ItemGroup/ProjectReference[string-length(@Include) >= $($tailValue.Length) and substring(@Include, string-length(@Include) - $($tailValue.Length - 1)) = '${tailValue}')]"
    
    Select-Xml -Path "/path/to/file.csproj" -XPath $xPathEndsWithExpression