views:

798

answers:

5

I'm trying a very basic XPath on this xml, 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).

A: 

I think the namespace is confusing things. Look in your API docs how to deal with namespaces.

Chris Farmer
+1  A: 

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
A: 

Please show us the code you're using, since I suspect it has more to do with how you're interpreting the results ... In your example, PropertyGroup should return a list with a single object and if you execute a "get text" method on it, you should return the contents of the MSBuildCommunityTasksPath since that is the only sub-element.

Steve Moyer
+1  A: 

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""&gt;
    <!-- $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
+8  A: 

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']
bill weaver
Thankyou! You've no idea how long I've hunted for this answer. Brilliant!
Gerard
This answer just solved a problem I was having, thanks!
Abel Morelos
@Abel Morelos - glad it helped; when i finally discovered this many moons ago it made XML work **so** much easier.
bill weaver