How to query default namespace with MSXML

Ian Boyd picture Ian Boyd · May 10, 2013 · Viewed 8k times · Source

I have some XML:

<?xml version="1.0" ?>
<Project ToolsVersion="4.0">
    <PropertyGroup Condition="'$(key)'=='1111'">
          <Key>Value</Key>
    </PropertyGroup>
</Project>

Note: This isn't the actual XML i'm using, it's just prettier and shorter, and demonstrates the problem.

Using MSXML i can query for nodes:

IXMLDOMNode node = doc.selectSingleNode("//PropertyGroup/@Condition");

And it works fine:

Condition="'$(key)'=='1111'"

But that's not really the XML i have

In reality the XML i have contains a namespace declaration:

xmlns="http://schemas.microsoft.com/developer/msbuild/2003"

making the actual XML document:

<?xml version="1.0" ?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
    <PropertyGroup Condition="'$(key)'=='1111'">
          <Key>Value</Key>
    </PropertyGroup>
</Project>

Now my query:

IDOMNode node = doc.selectSingleNode("//PropertyGroup/@Condition");

returns no matching nodes.

How do i query the default namespace using MSXML?

Note:

  • i already know how to query the non-default namespace in xml; you use:

       doc.setProperty("SelectionNamespaces", 
             "xmlns="http://schemas.microsoft.com/developer/msbuild/2003");
    
  • i already know how to query the default namespace in .NET. You use the namespace manager, give the default namespace a name, then query using that name, then you can query the non-default namespace since it's no longer default

  • i can just delete the offensive xmlns text from the XML string i receive, but i'd rather "do it the right way"

How do i query the "default", or "unnamed" namespace using MSXML?


Note: In reality the XML i am using the SQL Server's XML ShowPlan output:

<?xml version="1.0" encoding="UTF-16" standalone="yes"?>
   <ShowPlanXML Version="1.1" Build="10.50.1600.1" 
                   xmlns="http://schemas.microsoft.com/sqlserver/2004/07/showplan">
      <BatchSequence>
           <Batch>
           ...
           </Batch>
      </BatchSequence>
   </ShowPlanXML> 

Again you can see the offending namespace declaration. Deleting it works, but that's tedious.

What else have you tried?

i also tried setting the SelectionNamespace:

doc.setProperty('SelectionNamespaces', 
      'xmlns="http://schemas.microsoft.com/developer/msbuild/2003"');

as Microsoft hints at in a KB article.

How do i get the default namespace?

In reality i don't care about namespaces. My query makes sense, and i want it to work. So, Another approach to the question might be:

How can i query the default namespace whether, or not, and no matter what, that namespace name is (or isn't)?

Note: msxml is native code, and using it from a native Win32 compiler (i.e. no .NET framework or CLR)

Answer

granadaCoder picture granadaCoder · May 10, 2013

Explicitly give the namespace a name when you add it to the SelectionNamespaces:

doc.setProperty("SelectionNamespaces",
      "xmlns:peanut='http://schemas.microsoft.com/developer/msbuild/2003'");

and then query using that namespace:

IDOMNode node = doc.selectSingleNode("//peanut:PropertyGroup/@Condition");

You can give that namespace any abbreviation name you want (peanut in this case). And then use the abbreviation as prefix (peanut:PropertyGroup in this case).

Earlier suggestions

I would try moving to Xml.Linq.

Here is a sample (with a namespace).

      try
    {

        XDocument xDoc1 = XDocument.Parse("<?xml version=\"1.0\" ?><Project ToolsVersion=\"4.0\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\"><PropertyGroup Condition=\"'$(key)'=='1111'\"><Key>Value</Key></PropertyGroup></Project>");
        XNamespace ns1 = XNamespace.Get("http://schemas.microsoft.com/developer/msbuild/2003");

        var list1 = from list in xDoc1.Descendants(ns1 + "Project")
                    from item in list.Elements(ns1 + "PropertyGroup")
                    /* where item.Element(ns + "HintPath") != null */
                    where item.Attribute("Condition") != null
                    select new
                    {
                        MyCondition = item.Attribute("Condition") == null ? "Not Here!" : item.Attribute("Condition").Value,
                        MyFake = item.Attribute("DoesNotExistTest") == null ? "Not Here Sucker!" : item.Attribute("DoesNotExistTest").Value
                    };


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


        XDocument xDoc2 = XDocument.Parse("<?xml version=\"1.0\" encoding=\"UTF-16\" standalone=\"yes\"?>   <ShowPlanXML Version=\"1.1\" Build=\"10.50.1600.1\"                    xmlns=\"http://schemas.microsoft.com/sqlserver/2004/07/showplan\">      <BatchSequence>            <Batch>Something I Threw In Here</Batch>      </BatchSequence>    </ShowPlanXML> ");
        XNamespace ns2 = XNamespace.Get("http://schemas.microsoft.com/sqlserver/2004/07/showplan");

        var list2 = from list in xDoc2.Descendants(ns2 + "ShowPlanXML")
                    from item in list.Elements(ns2 + "BatchSequence")
                    /*                             where item.Attribute("Condition") != null */
                    where item.Element(ns2 + "Batch") != null 
                    select new
                    {
                        BatchValue = (item.Element(ns2 + "Batch") == null) ? string.Empty : item.Element(ns2 + "Batch").Value
                    };


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



    }
    catch (Exception ex)
    {
        Console.WriteLine(ex.Message);
    }