使用XML命名空间时简化语法

时间:2016-06-27 06:54:08

标签: c# .net xml vb.net

在一个简单的 .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>

我想存储一组<PropertyGroup>元素。我发现的问题是每次调用 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指定命名空间(或者不是一次),而是使用 XDocument.XPathSelectElements() ,但是,似乎我不能使用XPATH v2.0来忽略命名空间像//*:Project/PropertyGroup(或者我的语法错了?)。

有人可以解释我怎么能重构它?。

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

3 个答案:

答案 0 :(得分:1)

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

XNamespace yourns = "http://schemas.microsoft.com/developer/msbuild/2003";

IEnumerable<XElement> propGroups = 
    doc.Element(yourns + "Project").
        Elements(yourns + "PropertyGroup");

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

答案 1 :(得分:1)

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

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

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

答案 2 :(得分:0)

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

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

但是,如果层次较大,则重复字符串&#34; //*[local-name()=&#34;与指定命名空间的所有时间相比,将导致或多或少相同的annonying语法。无论如何,我比使用 LINQ-to-Objects 查询更容易使用XPath。

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

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