Как запросить пространство имен по умолчанию с помощью MSXML

У меня есть немного XML:

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

Примечание: это не фактический XML, который я использую, он просто красивее и короче и демонстрирует проблему.

Используя MSXML, я могу запрашивать узлы:

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

И это прекрасно работает

Condition="'$(ключ)'=='1111'"

Но это не совсем тот XML, который у меня есть

На самом деле XML, который я имею, содержит объявление пространства имен:

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

сделать настоящий документ XML:

<?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>

Теперь мой запрос:

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

не возвращает подходящих узлов.

Как я могу запросить пространство имен по умолчанию, используя MSXML?

Примечание:

  • я уже знаю, как запросить пространство имен не по умолчанию в XML; ты используешь:

       doc.setProperty("SelectionNamespaces", 
             "xmlns="http://schemas.microsoft.com/developer/msbuild/2003");
    
  • я уже знаю, как запросить пространство имен по умолчанию в.NET. Вы используете диспетчер пространства имен, присваиваете пространству имен по умолчанию имя, затем запрашиваете это имя, а затем можете запросить пространство имен не по умолчанию, поскольку оно больше не по умолчанию

  • я могу просто удалить наступление xmlns текст из XML-строки, которую я получаю, но я бы предпочел "сделать это правильно"

Как запросить пространство имен по умолчанию или без имени с использованием MSXML?


Примечание. На самом деле в XML я использую вывод SQL Server XML ShowPlan:

<?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> 

Снова вы можете увидеть оскорбительную декларацию пространства имен. Удаление работает, но это утомительно.

Что еще ты пробовал?

я также попытался установить SelectionNamespace:

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

как Microsoft намекает на в статье KB.

Как мне получить пространство имен по умолчанию?

На самом деле меня не волнуют пространства имен. Мой запрос имеет смысл, и я хочу, чтобы он работал. Итак, еще один подход к вопросу может быть:

Как я могу запросить пространство имен по умолчанию, или нет, и неважно, что это имя пространства имен (или нет)?

Примечание: msxml является нативным кодом и использует его из нативного компилятора Win32 (т.е. без.NET Framework или CLR)

2 ответа

Решение

Явно дайте пространству имен имя, когда вы добавите его в SelectionNamespaces:

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

а затем выполнить запрос, используя это пространство имен:

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

Вы можете дать этому пространству имен любое сокращенное имя, которое хотите (peanut в этом случае). А затем используйте сокращение в качестве префикса (peanut:PropertyGroup в этом случае).

Предыдущие предложения

Я бы попробовал переехать в Xml.Linq,

Вот пример (с пространством имен).

      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);
    }

Если вы похожи на меня и не хотите менять свое.SelectNodes("//Test")код, соответствующий каждому возможному пространству имен, вы можете обратиться к моему ответу на вопрос « Как игнорировать пространство имен XML».

В основном я использовал метод.transformNodeToObject так :

      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
Другие вопросы по тегам