【问题标题】:Simplify syntax when working with XML namespaces使用 XML 命名空间时简化语法
【发布时间】:2016-06-27 06:54:08
【问题描述】:

在一个简单的 .vbproj 文件中:

<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
  <PropertyGroup>
    ...
  </PropertyGroup>
  <Import Project="$(MSBuildToolsPath)\Microsoft.VisualBasic.targets" />
</Project>

我想存储&lt;PropertyGroup&gt; 元素的集合。我发现的问题是每次调用 XDocument.Element()XDocument.Elements() 函数时都需要指定(长)命名空间:

string text = File.ReadAllText("My project.vbproj");
XDocument doc = XDocument.Parse(text, LoadOptions.None);

IEnumerable<XElement> propGroups = 
    doc.Element("{http://schemas.microsoft.com/developer/msbuild/2003}Project").
        Elements("{http://schemas.microsoft.com/developer/msbuild/2003}PropertyGroup");

我很确定应该存在一种更友好的语法来简化上面的代码,也许只调用一次 XDocument.Elements() 而不是 XDocument.Element(),或者也许为了避免使用适当的 XPath 指定命名空间(或 mroe 不止一次)并使用 XDocument.XPathSelectElements() 代替,但是,似乎我不能使用 XPATH v2.0 来忽略 //*:Project/PropertyGroup 之类的命名空间(或也许我的语法错了?)。

谁能解释我如何重构它?

请注意,目的是修改 Xml 的某些属性,然后将更改保存在文件中,而不仅仅是获取 Xml 中的数据(如果这对解决方案很重要)。

【问题讨论】:

  • 你看过XNamespace吗?
  • @Tim 感谢您的参考,从我看到的 MSDN 代码示例中显示的解决方案与我所做的相同,但只是使用 var 来引用命名空间名称。老实说,我期望其他类型的解决方案比 MSDN 显示的更合适。因为本质上它显示了相同的名称空间字符串,需要一直指定/连接。
  • XName 怎么样?它具有本地名称(没有命名空间)的属性,以及相关 XNamespace 的命名空间和 URI。
  • 我通常使用这个:IEnumerable propGroups = doc.Descendants().Where(x => x.Name.LocalName == "PropertyGroup");

标签: c# .net xml vb.net


【解决方案1】:

如果您对明确指出ProjectPropertyGroup 的直接父代不是特别感兴趣,则可以改为使用Descendants 来查找所有PropertyGroup 元素,无论它们出现在文档中的什么位置:

XNamespace ns = "http://schemas.microsoft.com/developer/msbuild/2003";
var propGroups = doc.Descendants(ns + "PropertyGroup");

API 的目的是使用 + 运算符将命名空间与本地名称结合起来,以避免为每个名称都包含它的冗长。如果要识别使用命名空间的元素,没有简单的方法可以避免使用命名空间。按本地名称过滤比这要详细得多。

【讨论】:

    【解决方案2】:

    您可以将命名空间字符串放入变量中并以这种方式使用它:

    XNamespace yourns = "http://schemas.microsoft.com/developer/msbuild/2003";
    
    IEnumerable<XElement> propGroups = 
        doc.Element(yourns + "Project").
            Elements(yourns + "PropertyGroup");
    

    我认为这是一个更具可读性和可重用性的解决方案。

    【讨论】:

    • 感谢您的回答,但是在您给出的示例中仍然存在两次指定命名空间的需要,就像我提供的原始代码一样,只是方式不同。老实说,在发布问题中的代码时,我错过了将字符串放入 var 的事实,但我期望其他类型的解决方案并不意味着这样做。
    • 你应该从命名空间中删除{},因为这不是它的一部分。
    • 你是对的!我做了一个糟糕的复制和粘贴。对不起。我编辑了答案。谢谢@CharlesMager。
    【解决方案3】:

    我终于发现了 XPath v1.0 的正确用法,这样完全避免了编写中间命名空间:

    Dim propGroups As IEnumerable(Of XElement) =
        doc.XPathSelectElements("//*[local-name()='Project']//*[local-name()='PropertyGroup']")
    

    但是,在大层次结构的情况下,重复的字符串“//*[local-name()=”将导致与始终指定命名空间相比,或多或少相同的烦人语法。无论如何,我更喜欢 XPath 的使用,而不是 LINQ-to-Objects 查询。

    我不会接受我自己的答案,因为我不完全确定 XPath 语法是否已尽可能简化。

    同样在 XPath v2.0 中会更加简化,但是,.NET 似乎不支持它(或者我可能在测试时使用了错误的语法)。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-04-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多