Лучшее решение, чем element.Elements («Безотносительно»). First ()? - PullRequest
1 голос
/ 18 апреля 2010

У меня есть XML-файл, подобный этому:

<SiteConfig>
  <Sites>
    <Site Identifier="a" />
    <Site Identifier="b" />
    <Site Identifier="c" />
  </Sites>
</SiteConfig>

Файл доступен для редактирования пользователем, поэтому я хочу предоставить разумное сообщение об ошибке на случай, если я не смогу его правильно проанализировать. Я мог бы написать .xsd для него, но это кажется излишним для простого файла.

Так или иначе, когда я запрашиваю список <Site> узлов, я могу сделать это несколькими способами:

var doc = XDocument.Load(...);

var siteNodes = from siteNode in 
                  doc.Element("SiteConfig").Element("Sites").Elements("Site")
                select siteNode;

Но проблема в том, что если пользователь не включил узел <SiteUrls> (скажем), он просто выдаст NullReferenceException, который мало что говорит пользователю о том, что на самом деле пошло не так.

Другая возможность - это просто использовать Elements() везде вместо Element(), но это не всегда срабатывает при соединении со звонками на Attribute(), например, в следующей ситуации:

var siteNodes = from siteNode in 
                  doc.Elements("SiteConfig")
                     .Elements("Sites")
                     .Elements("Site")
                where siteNode.Attribute("Identifier").Value == "a"
                select siteNode;

(то есть, нет эквивалента Attributes("xxx").Value)

Есть ли что-то встроенное в каркас, чтобы немного лучше справиться с этой ситуацией? Я бы предпочел версию Element()Attribute(), пока мы на ней), которая выдает описательное исключение (например, «Поиск элемента в , но такой элемент не найден») вместо возврата null.

Я мог бы написать свою собственную версию Element() и Attribute(), но мне просто кажется, что это настолько распространенный сценарий, что я должен что-то упустить ...

Ответы [ 2 ]

2 голосов
/ 18 апреля 2010

Вы можете реализовать желаемую функциональность в качестве метода расширения:

public static class XElementExtension
{
    public static XElement ElementOrThrow(this XElement container, XName name)
    {
        XElement result = container.Element(name);
        if (result == null)
        {
            throw new InvalidDataException(string.Format(
                "{0} does not contain an element {1}",
                container.Name,
                name));
        }
        return result;
    }
}

Вам понадобится нечто подобное для XDocument. Затем используйте это так:

var siteNodes = from siteNode in 
    doc.ElementOrThrow("SiteConfig")
       .ElementOrThrow("SiteUrls")
       .Elements("Sites")
    select siteNode;

Тогда вы получите следующее исключение:

SiteConfig does not contain an element SiteUrls
0 голосов
/ 18 апреля 2010

Вы можете использовать XPathSelectElements

using System;
using System.Linq;
using System.Xml.Linq;
using System.Xml.XPath;

class Program
{
    static void Main()
    {
        var ids = from site in XDocument.Load("test.xml")
                  .XPathSelectElements("//SiteConfig/Sites/Site")
                  let id = site.Attribute("Identifier")
                  where id != null
                  select id;
        foreach (var item in ids)
        {
            Console.WriteLine(item.Value);
        }
    }
}

Еще одна вещь, которая приходит на ум, это определить схему XSD и проверить ваш XML-файл на соответствие этой схеме. Это приведет к значимым сообщениям об ошибках, и если файл действителен, вы можете проанализировать его без проблем.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...