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.

Upvotes: 1

Views: 71

Answers (3)

js2010
js2010

Reputation: 27516

I think I've got Xpath 2.0 working with Saxon HE 10.9 for .net. You have to install that setup file first to get the dll. I haven't gotten downloading the dll through nuget working.

# https://github.com/Saxonica/Saxon-HE/blob/main/10/Dotnet/SaxonHE10-9N-setup.exe

Add-Type -Path "C:\Program Files\Saxonica\SaxonHE10.9N\bin\saxon-he-api-10.9.dll"

# Initialize Saxon Processor and Components
$processor = New-Object Saxon.Api.Processor
$documentBuilder = $processor.NewDocumentBuilder()
$xpathCompiler = $processor.NewXPathCompiler()

# Load XML Document
$doc = $documentBuilder.Build("$pwd\file.csproj")

# Compile the XPath Expression
$xpathExpression = $xpathCompiler.Compile(
  "/Project/ItemGroup/ProjectReference[ends-with(@Include, 'Something.csproj')]")

# Create an XPath Selector
$xpathSelector = $xpathExpression.Load()

# Set the context for the XPath Selector
$xpathSelector.ContextItem = $doc

# Evaluate the XPath Expression
$result = $xpathSelector.Evaluate()

# Output Results
foreach ($item in $result) {
    Write-Output $item.ToString()
}

Output:

<ProjectReference Include="Path\To\Something.csproj"/>

Upvotes: 0

SalgoMato
SalgoMato

Reputation: 329

Inspecting code of Select-Xml cmdlet I found out that internally it uses SelectNodes method of NET class System.Xml.XmlNode. Thus you can find list of recognized functions in this Microsoft XPath reference documentation: XPath Reference

Upvotes: 0

Mathias R. Jessen
Mathias R. Jessen

Reputation: 174825

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

Upvotes: 1

Related Questions