10

I'm trying a very basic XPath on this xml (same as below), and it doesn't find anything. I'm trying both .NET and this website, and XPaths such as //PropertyGroup, /PropertyGroup and //MSBuildCommunityTasksPath are simply not working for me (they compiled but return zero results).

Source XML:

<?xml version="1.0" encoding="utf-8"?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
    <!-- $Id: FxCop.proj 114 2006-03-14 06:32:46Z pwelter34 $ -->
    <PropertyGroup>
        <MSBuildCommunityTasksPath>$(MSBuildProjectDirectory)\MSBuild.Community.Tasks\bin\Debug</MSBuildCommunityTasksPath>
    </PropertyGroup>
    <Import
        Project="$(MSBuildProjectDirectory)\MSBuild.Community.Tasks\MSBuild.Community.Tasks.Targets" />
    <Target Name="DoFxCop">
        <FxCop TargetAssemblies="$(MSBuildCommunityTasksPath)\MSBuild.Community.Tasks.dll"
            RuleLibraries="@(FxCopRuleAssemblies)" 
            AnalysisReportFileName="Test.html"
            DependencyDirectories="$(MSBuildCommunityTasksPath)" 
            FailOnError="True"
            ApplyOutXsl="True"
            OutputXslFileName="C:\Program Files\Microsoft FxCop 1.32\Xml\FxCopReport.xsl" />
    </Target>
</Project>
Wayne
  • 59,728
  • 15
  • 131
  • 126
ripper234
  • 222,824
  • 274
  • 634
  • 905
  • If you could provide a short code sample of what you have tried in .net it will make it easier to suggest what might be going wrong. – morechilli Sep 19 '08 at 16:49

3 Answers3

16

You can add namespaces in your code and all that, but you can effectively wildcard the namespace. Try the following XPath idiom.

//*[local-name()='PropertyGroup']
//*[local-name()='MSBuildCommunityTasksPath']

name() usually works as well, as in:

//*[name()='PropertyGroup']
//*[name()='MSBuildCommunityTasksPath']

EDIT: Namespaces are great and i'm not suggesting they're not important, but wildcarding them comes in handy when cobbling together prototype code, one-off desktop tools, experimenting with XSLT, and so forth. Balance your need for convenience against acceptable risk for the task at hand. FYI, if need be, you can also strip or reassign namespaces.

b w
  • 4,553
  • 4
  • 31
  • 36
  • Thankyou! You've no idea how long I've hunted for this answer. Brilliant! –  Jul 16 '09 at 03:53
  • @Abel Morelos - glad it helped; when i finally discovered this many moons ago it made XML work **so** much easier. – b w Feb 26 '10 at 17:26
  • Unqualifying is a bad thing to do. It's equivalent to asking for columns from a RDBMS without specifying which tables. How does one line of code for each prefix declaration turn into "and all that"? – grantwparks Aug 07 '11 at 18:59
  • @grantwparks - i'm not saying namespaces aren't valuable, but sometimes it's necessary to build a transform that ignores them, and sometimes it's just convenient to not deal with them. Whatever the case, this solution comes in handy. – b w Aug 08 '11 at 15:38
  • HOURS of searching terminated here. THANK YOU! – Mike Trader Feb 21 '12 at 20:23
2

The tags in the document end up in the "default" namespace created by the xmlns attribute with no prefix. Unfortunately, XPath alone can not query elements in the default namespace. I'm actually not sure of the semantic details, but you have to explicitly attach a prefix to that namespace using whatever tool is hosting XPath.

There may be a shorter way to do this in .NET, but the only way I've seen is via a NameSpaceManager. After you explicitly add a namespace, you can query using the namespace manager as if all the tags in the namespaced element have that prefix (I chose 'msbuild'):

using System;
using System.Xml;

public class XPathNamespace {
    public static void Main(string[] args) {
        XmlDocument xmlDocument = new XmlDocument();
        xmlDocument.LoadXml(
    @"<?xml version=""1.0"" encoding=""utf-8""?>
<Project xmlns=""http://schemas.microsoft.com/developer/msbuild/2003"">
    <!-- $Id: FxCop.proj 114 2006-03-14 06:32:46Z pwelter34 $ -->

    <PropertyGroup>
        <MSBuildCommunityTasksPath>$(MSBuildProjectDirectory)\MSBuild.Community.Tasks\bin\Debug</MSBuildCommunityTasksPath>
    </PropertyGroup>

    <Import Project=""$(MSBuildProjectDirectory)\MSBuild.Community.Tasks\MSBuild.Community.Tasks.Targets""/>

    <Target Name=""DoFxCop"">

        <FxCop 
            TargetAssemblies=""$(MSBuildCommunityTasksPath)\MSBuild.Community.Tasks.dll""
            RuleLibraries=""@(FxCopRuleAssemblies)"" 
            AnalysisReportFileName=""Test.html""
            DependencyDirectories=""$(MSBuildCommunityTasksPath)""
            FailOnError=""True""
            ApplyOutXsl=""True""
            OutputXslFileName=""C:\Program Files\Microsoft FxCop 1.32\Xml\FxCopReport.xsl""
        />
    </Target>

</Project>");

        XmlNamespaceManager namespaceManager = new
    XmlNamespaceManager(xmlDocument.NameTable);
        namespaceManager.AddNamespace("msbuild", "http://schemas.microsoft.com/developer/msbuild/2003");
        foreach (XmlNode n in xmlDocument.SelectNodes("//msbuild:MSBuildCommunityTasksPath", namespaceManager)) {
            Console.WriteLine(n.InnerText);
        }
    }
}
Jesse Millikan
  • 3,104
  • 1
  • 21
  • 32
1

Your issue is with the namespace (xmlns="http://schemas.microsoft.com/developer/msbuild/2003"). You're receiving zero nodes because you aren't qualifying it with the namespace. If you remove the xmlns attribute, your "//PropertyGroup" XPath will work. How you query with namespace usually involves aliasing a default xmlns to an identifier (since one is not specified on the attribute), and selecting like "//myXMLNStoken:PropertyGroup".

Pseudo Masochist
  • 1,927
  • 14
  • 12