13

I am for sure missing some important detail here. I just cannot make .NET's XPath work with Visual Studio project files.

Let's load an xml document:

var doc = new XmlDocument();
doc.Load("blah/blah.csproj");

Now execute my query:

var nodes = doc.SelectNodes("//ItemGroup");
Console.WriteLine(nodes.Count); // whoops, zero

Of course, there are nodes named ItemGroup in the file. Moreover, this query works:

var nodes = doc.SelectNodes("//*/@Include");
Console.WriteLine(nodes.Count); // found some

With other documents, XPath works just fine. I am absolutely puzzled about that. Could anyone explain me what is going on?

Strelok
  • 50,229
  • 9
  • 102
  • 115
catbert
  • 1,017
  • 1
  • 9
  • 19
  • How about `var nodes = doc.SelectNodes("//*/ItemGroup");` I'm in Mac OS X now so cannot verify. – Strelok Sep 19 '10 at 08:38

3 Answers3

16

You probably need to add a reference to the namespace http://schemas.microsoft.com/developer/msbuild/2003.

I had a similar problem, I wrote about it here. Do something like this:

XmlDocument xdDoc = new XmlDocument();
xdDoc.Load("blah/blah.csproj");

XmlNamespaceManager xnManager =
 new XmlNamespaceManager(xdDoc.NameTable);
xnManager.AddNamespace("tu",
 "http://schemas.microsoft.com/developer/msbuild/2003");

XmlNode xnRoot = xdDoc.DocumentElement;
XmlNodeList xnlPages = xnRoot.SelectNodes("//tu:ItemGroup", xnManager);
Naeem Sarfraz
  • 7,360
  • 5
  • 37
  • 63
13

Look at the root namespace; you'll have to include an xml-namespace manager and use queries like "//x:ItemGroup", where "x" is your designated alias for the root namespace. And pass the manager into the query. For example:

        XmlDocument doc = new XmlDocument();
        doc.Load("my.csproj");

        XmlNamespaceManager mgr = new XmlNamespaceManager(doc.NameTable);
        mgr.AddNamespace("foo", doc.DocumentElement.NamespaceURI);
        XmlNode firstCompile = doc.SelectSingleNode("//foo:Compile", mgr);
Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
  • Adding a namespace did the trick. Pity that one cannot accept two answers for a question :-) – catbert Sep 19 '10 at 08:58
  • 1
    @catbert: you can reverse your currently-accepted answer (which is wrong), and accept the correct (@Marc-Gravell 's answer). Not only you can, but you `should` do so, because otherwise some people will think that the wrong/accepted answer is correct. – Dimitre Novatchev Sep 19 '10 at 20:45
5

I posted a LINQ / Xml version over at:

http://granadacoder.wordpress.com/2012/10/11/how-to-find-references-in-a-c-project-file-csproj-using-linq-xml/

But here is the gist of it. It may not be 100% perfect......but it shows the idea.

I'm posting the code here, since I found this (original post) when searching for an answer. Then I got tired of searching and wrote my own.

using System;
using System.Linq;
using System.Xml.Linq;

            string fileName = @"C:\MyFolder\MyProjectFile.csproj";

            XDocument xDoc = XDocument.Load(fileName);

            XNamespace ns = XNamespace.Get("http://schemas.microsoft.com/developer/msbuild/2003");

            //References "By DLL (file)"
            var list1 = from list in xDoc.Descendants(ns + "ItemGroup")
                        from item in list.Elements(ns + "Reference")
                        /* where item.Element(ns + "HintPath") != null */
                    select new
                       {
                           CsProjFileName = fileName,
                           ReferenceInclude = item.Attribute("Include").Value,
                           RefType = (item.Element(ns + "HintPath") == null) ? "CompiledDLLInGac" : "CompiledDLL",
                           HintPath = (item.Element(ns + "HintPath") == null) ? string.Empty : item.Element(ns + "HintPath").Value
                       };


            foreach (var v in list1)
            {
                Console.WriteLine(v.ToString());
            }


            //References "By Project"
            var list2 = from list in xDoc.Descendants(ns + "ItemGroup")
                        from item in list.Elements(ns + "ProjectReference")
                        where
                        item.Element(ns + "Project") != null
                        select new
                        {
                            CsProjFileName = fileName,
                            ReferenceInclude = item.Attribute("Include").Value,
                            RefType = "ProjectReference",
                            ProjectGuid = item.Element(ns + "Project").Value
                        };


            foreach (var v in list2)
            {
                Console.WriteLine(v.ToString());
            }
granadaCoder
  • 26,328
  • 10
  • 113
  • 146
  • not 100% perfect? It suited me fine but what would be the pitfalls if you dont mind me asking after 2 years – mechanicum Jul 09 '14 at 12:45
  • I don't know what it does with GAC references, that's the only unknown, because I rarely do GAC references outside the main framework. That's about it. I still use the script when I need a poor-man's "references report" ... – granadaCoder Jul 09 '14 at 12:53