Подтвердить что ты не робот

Запрос 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?

4b9b3361

Ответ 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 я не знал, что я делал неправильно часами, когда забыл предоставить пространство имен по умолчанию.