How to query default namespace with MSXML
Asked Answered
P

2

15

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)

Prolific answered 10/5, 2013 at 21:6 Comment(3)
I made one more update. It hit me while driving home from the movie theater.Irbm
@Irbm You were answering StackOverflow while watching a movie!?Prolific
On the drive home, I gave a few of my CPU "cycles" to this thing.....my radio just died on me.Irbm
I
16

Explicitly give the namespace an abbreviated-alias 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 (but referring to it by its abbreviated-alias):

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

You can give that namespace any abbreviated-alias you want (here I use value of peanut to demonstrate it can be any value you want for the value of the abbreviated-alias).
And then use that same abbreviated-alias as a prefix (peanut:PropertyGroup in this case) in the "search-criteria".

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);
    }
Irbm answered 10/5, 2013 at 21:20 Comment(5)
Sorry, IXMLDOMNode, not IDOMNode. It's from native msxml COM object library. And this is from native not (i.e. not .NET)Prolific
Ok. Check my update for a hint. I'm guessing, but figure that's better than leaving the just .Net response.Irbm
i forgot to give you the answer; that worked. It's horribly awful that you have to name the default namespace before you can use it. But at least it's a workable solution!Prolific
It would be ambiguous (and cause potential conflicts) between "no namespace" and "default namespace" if you didn't give the default namespace an alias. I agree it is not intuitive, but it isn't unnecessary. Don't forget the "Mark as Answer" part (so others don't visit this query unnecessarily)Irbm
Oh christ; i upvoted but forgot the accept. How long have i been here? Four years? /facepalmProlific
F
0

If you are like me, and don't want to change your .SelectNodes("//Test") code to match every possible namespace, you can refer to my answer to How to ignore an XML namespace

Basically, I used the method .transformNodeToObject like this :

Public Sub fixNS(ByRef doc As DOMDocument60)

  ' Create a new XML document (fixNS0) that contains an XSLT style sheet.
  ' The XSLT style sheet will remove namespace prefixes from XML elements and attributes.
  ' The resulting XML document will have the same content as the original, but with the namespace prefixes removed.
  Dim fixNS0 As New DOMDocument60

  fixNS0.LoadXML ("<xsl:stylesheet version='1.0' xmlns:xsl='http://www.w3.org/1999/XSL/Transform'>" & _
    "<xsl:output method='xml' indent='yes' omit-xml-declaration='yes' />" & _
    "<xsl:template match='comment()'> <xsl:copy/> </xsl:template>" & _
    "<xsl:template match='*'>" & _
      "<xsl:text>&#xA;</xsl:text>" & _
      "<xsl:element name='{local-name(.)}'> <xsl:apply-templates select='@* | node()'/> </xsl:element>" & _
      "<xsl:text>&#xA;</xsl:text>" & _
    "</xsl:template> <xsl:template match='@*'>" & _
      "<xsl:attribute name='{local-name(.)}'> <xsl:value-of select='.'/> </xsl:attribute>" & _
    "</xsl:template> </xsl:stylesheet>")

  ' Apply the XSLT style sheet (in fixNS0) to the XML document (in doc).
  ' The result of the transformation is written back to the same XML document (in doc).
  doc.transformNodeToObject fixNS0, doc

End Sub
Feoffee answered 7/3, 2023 at 19:21 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.