Запрос XDocument с атрибутом xmlns (пространство имен)
Я пытаюсь запросить элементы из 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?
Ответы
Ответ 1
Почему это проблема, когда XML-документ содержит атрибут xmlns?
Это не так, если вы понимаете, что это значит:) В основном вы применили URI-адрес пространства имен по умолчанию "http://schemas.microsoft.com/developer/msbuild/2003" ко всем элементам. Поэтому при запросе вам необходимо указать это пространство имен. К счастью, 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);
}
Ответ 2
Не обязательно знать пространство имен заранее.
Вы можете написать код, который работает с обоими Xmls, потому что вы можете получить пространство имен по умолчанию из XElement.
XDocument doc = XDocument.Parse(xml2);
XNamespace ns = doc.Root.GetDefaultNamespace();
foreach (XElement element in doc.Descendants(ns + "ItemGroup"))
{
Console.WriteLine(element);
}
Я также написал метод расширения для разрешения XName из любого объекта XObject (XElement, XDocument и т.д.).
Преимущество использования метода расширения вместо 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 - замечательный API. Но я думаю, что ясно, что если я не предоставляю пространство имен, я всегда имею в виду пространство имен по умолчанию. Я не вижу причин, почему LINQ to XML не ведет себя так. Это немного недостаток, который меня очень раздражал. И впервые в качестве новичка с LINQ to XML я не знал, что я делал неправильно часами, когда забыл предоставить пространство имен по умолчанию.