18

我尝试从 Visual Studio *.csproj 文件中查询元素。我创建了一个简短的示例来说明问题:

    // Working
    string xml1 = @"<Project ToolsVersion='4.0'>
                      <ItemGroup Label='Usings'>
                        <Reference Include='System' />
                        <Reference Include='System.Xml' />
                      </ItemGroup>
                    </Project>"; 
    // Not working
    string xml2 = @"<Project ToolsVersion='4.0' xmlns='http://schemas.microsoft.com/developer/msbuild/2003'>
                      <ItemGroup Label='Usings'>
                        <Reference Include='System' />
                        <Reference Include='System.Xml' />
                      </ItemGroup>
                    </Project>";

    XDocument doc = XDocument.Parse(xml2);

    foreach (XElement element in doc.Descendants("ItemGroup"))
    {
        Console.WriteLine(element);
    }

字符串 xml1 工作正常, xml2 不返回任何内容。这些字符串之间的唯一区别是文档根目录中的 xmlns 属性。

如何查询包含 xmlns 属性的文档?为什么当 xml 文档包含 xmlns 属性时会出现问题?

4

2 回答 2

23

为什么当 xml 文档包含 xmlns 属性时会出现问题?

不是,如果您理解它的含义 :) 基本上,您已将“ http://schemas.microsoft.com/developer/msbuild/2003 ”的默认命名空间 URI应用于所有元素。因此,在查询时,您也需要指定该命名空间。幸运的是,LINQ to XML 让这变得非常简单:

XNamespace ns = "http://schemas.microsoft.com/developer/msbuild/2003";
XDocument doc = XDocument.Parse(xml2);
foreach (XElement element in doc.Descendants(ns + "ItemGroup"))
{
    Console.WriteLine(element);
}
于 2013-04-15T14:59:15.153 回答
7

不必事先知道命名空间。您可以编写适用于这两种 Xml 的代码,因为您可以从 XElement 获取默认命名空间。

XDocument doc = XDocument.Parse(xml2);
XNamespace ns = doc.Root.GetDefaultNamespace();
foreach (XElement element in doc.Descendants(ns + "ItemGroup"))
{
    Console.WriteLine(element);
}

我还编写了一个扩展方法来从任何 XObject(XElement、XDocument 等)解析 XName。

使用扩展方法而不是 GetDefaultNamespace 的好处是您不必检查是否已经提供了另一个命名空间。

public static XName ResolveName(this XObject xObj, XName name)
{
    //If no namespace has been added, use default namespace anyway
    if (string.IsNullOrEmpty(name.NamespaceName))
    {
        name = xObj.Document.Root.GetDefaultNamespace() + name.LocalName;
    }
    return name;
}

你可以像这样使用它

XDocument doc = XDocument.Parse(xml2);
foreach (XElement element in doc.Descendants(doc.ResolveName("ItemGroup")))
{
    Console.WriteLine(element);
}

我认为 LINQ to XML 是一个很棒的 AP​​I。但我认为很清楚,如果我不提供命名空间,我总是指默认命名空间。我看不出 LINQ to XML 不这样做的任何原因。这是一个让我非常恼火的小缺点。第一次作为 LINQ to XML 的初学者,当我忘记提供默认命名空间时,我好几个小时都不知道自己做错了什么。

于 2016-09-14T21:52:42.520 回答