0

I'm struggling to use the contains(text,'some text') from XPATH.

I saw this link How to use XPath contains() but for some reason is not working for me

I need to remove a project reference, so I want to select by only using "ProjectName" (without specifying the full path) I took a script from this answer How do I use Powershell to add/remove references to a csproj? (see RemoveReference.ps1)

Here my code, and also a fiddle https://dotnetfiddle.net/Cq5fa6

using System;
using System.Xml;
                    
public class Program
{
    public static void Main()
    {
            //Declare the XML Document here
            XmlDocument Doc = new XmlDocument();

            //Load you xml here
            //Doc.LoadXml(TransportResponse.Response)
            Doc.LoadXml(@"<?xml version=""1.0"" encoding=""utf-8""?>
<Project ToolsVersion=""15.0"" xmlns=""http://schemas.microsoft.com/developer/msbuild/2003"">
  <ItemGroup>
    <ProjectReference Include=""..\ProjectReferencesToolkit.Core\ProjectReferencesToolkit.Core.csproj"">
      <Project>{6c167ddd-7ce8-4087-9f8c-6986145b97d1}</Project>
      <Name>ProjectReferencesToolkit.Core</Name>
    </ProjectReference>
  </ItemGroup>
  <Import Project=""$(MSBuildToolsPath)\Microsoft.CSharp.targets"" />
</Project>
");

//I need to use contains here to only search for ProjectReferencesToolkit.Core.csproj (not specifying full path)
var XPath = String.Format("//a:ProjectReference[@Include='{0}']","..\\ProjectReferencesToolkit.Core\\ProjectReferencesToolkit.Core.csproj");    
    var nsmgr = new XmlNamespaceManager(Doc.NameTable);
    nsmgr.AddNamespace("a","http://schemas.microsoft.com/developer/msbuild/2003");
    var node = Doc.SelectSingleNode(XPath, nsmgr);
        
Console.WriteLine(node.InnerXml);
        
    }
}

mspasiuk
  • 602
  • 1
  • 7
  • 23
  • You know, in general I'll say it is a bad idea... How do you escape string to an xpath string? Let's say that the filename is `My'Beautiful'Flower.csproj`... – xanatos Dec 28 '20 at 21:52
  • I don't have that kind of csproj filenames. – mspasiuk Dec 28 '20 at 22:02
  • Your life as a programmer must be very boring if you don't try some funny names in your projects or in your folders :-) :-) If you don't break the Explorer you aren't even playing :-) – xanatos Dec 28 '20 at 22:03

2 Answers2

0

I think it is a bad idea. Escaping strings in XPath is a pain.

Instead of contains I'm doing an ends-with, that should be even better for what you want. Clearly ends-with is XPath 2.0, and .NET only supports 1.0, so I have to use substring.

string fileName = "ProjectReferencesToolkit.Core.csproj";
var XPath = string.Format("//a:ProjectReference[substring(@Include, string-length(@Include) - {0} + 1) = '{1}']", fileName.Length, fileName);

And with a little escaping:

// https://stackoverflow.com/a/38254661/613130
public static string EscapeXPathString(string value)
{
    if (!value.Contains("'"))
    {
        return '\'' + value + '\'';
    }
    else if (!value.Contains("\""))
    {
        return '"' + value + '"';
    }
    else
    {
        return "concat('" + value.Replace("'", "',\"'\",'") + "')";
    }
}

In the xml:

<ProjectReference Include=""..\ProjectReferencesToolkit.Core\ProjectReferencesToolkit&quot;'.Core.csproj"">

and then:

string fileName = @"ProjectReferencesToolkit""'.Core.csproj";
string fileNameEscaped = EscapeXPathString(fileName);
var XPath = string.Format("//a:ProjectReference[substring(@Include, string-length(@Include) - {0} + 1) = {1}]", fileName.Length, fileNameEscaped);

(now that I've escape the XPath string I feel much better )

xanatos
  • 109,618
  • 12
  • 197
  • 280
0

It is better to use LINQ to XML API. It is available in the .Net Framework since 2007.

c#

void Main()
{
    XDocument xsdoc = XDocument.Parse(@"<?xml version='1.0' encoding='utf-8'?>
        <Project ToolsVersion='15.0'
                 xmlns='http://schemas.microsoft.com/developer/msbuild/2003'>
            <ItemGroup>
                <ProjectReference Include='..\ProjectReferencesToolkit.Core\ProjectReferencesToolkit.Core.csproj'>
                    <Project>{6c167ddd-7ce8-4087-9f8c-6986145b97d1}</Project>
                    <Name>ProjectReferencesToolkit.Core</Name>
                </ProjectReference>
            </ItemGroup>
            <Import Project='$(MSBuildToolsPath)\Microsoft.CSharp.targets'/>
        </Project>");
        
        string SearchFor = @"ProjectReferencesToolkit.Core.csproj";
        
        XNamespace ns = xsdoc.Root.GetDefaultNamespace();
        XElement xelem = xsdoc.Descendants(ns + "ProjectReference")
            .FirstOrDefault(x => x.Attribute("Include").Value.EndsWith(SearchFor));
        
        Console.WriteLine(xelem);
}
Yitzhak Khabinsky
  • 18,471
  • 2
  • 15
  • 21