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

Проблемы с чтением RSS с С# и .net 3.5

Я пытаюсь написать некоторые подпрограммы для чтения RSS-каналов и фидов ATOM с использованием новых подпрограмм, доступных в System.ServiceModel.Syndication, но, к сожалению, Rss20FeedFormatter запускает примерно половину каналов, которые я пытаюсь сделать со следующим исключением:

An error was encountered when parsing a DateTime value in the XML.

Это происходит, когда канал RSS выражает дату публикации в следующем формате:

Thu, 16 Oct 08 14:23:26 -0700

Если канал выражает дату публикации как GMT, все идет хорошо:

Thu, 16 Oct 08 21:23:26 GMT

Если есть способ обойти это с помощью XMLReaderSettings, я его не нашел. Может ли кто-нибудь помочь?

4b9b3361

Ответ 1

RSS 2.0 форматированные каналы синдикации используют спецификацию даты RFC 822 при сериализации таких элементов, как pubDate и lastBuildDate. Спецификация даты и времени RFC 822, к сожалению, является очень "гибким" синтаксисом для выражения компонента часового пояса DateTime.

Часовой пояс может указываться несколькими способами. "UT" - это универсальное время (ранее называемое "средним временем по Гринвичу" ); "GMT" разрешено в качестве ссылки на Universal Time. Военный стандарт использует один символ для каждой зоны. "Z" - это универсальное время. "A" указывает на час раньше, а "M" указывает на 12 часов раньше; "N" - через час, а "Y" - через 12 часов. Буква "J" не используется. Остальные две формы взяты из стандарта ANSI X3.51-1975. Один позволяет явно указывать величину смещения от UT; другой использует обычные 3-символьные строки для указания часовых поясов в Северной Америке.

Я полагаю, что проблема связана с тем, как обрабатывается компонент zone значения даты и времени RFC 822. Форматирование фида, похоже, не обрабатывает дату-время, в котором используется локальный дифференциал, чтобы указать часовой пояс.

Поскольку RFC 1123 расширяет спецификацию RFC 822, вы можете попробовать использовать DateTimeFormatInfo.RFC1123Pattern ( "r" ) для обработки конвертирования пробламатической даты- или написать собственный код анализа для даты форматирования RFC 822. Другим вариантом будет использование сторонней структуры вместо классов пространства имен System.ServiceModel.Syndication.

Похоже, существуют некоторые известные проблемы с синтаксисом даты и времени и Rss20FeedFormatter, которые находятся в процессе обращения Microsoft.

Ответ 2

Основываясь на обходном пути, опубликованном в отчете об ошибках в Microsoft об этом, я создал XmlReader специально для чтения SyndicationFeeds, которые имеют нестандартные даты,

Код ниже немного отличается от кода в обходном пути на сайте Microsoft. Он также принимает консультативный совет по использованию шаблона RFC 1123.

Вместо простого вызова XmlReader.Create() вам нужно создать XmlReader из Stream. Я использую класс WebClient для получения этого потока:

WebClient client = new WebClient();
using (XmlReader reader = new SyndicationFeedXmlReader(client.OpenRead(feedUrl)))
{
    SyndicationFeed feed = SyndicationFeed.Load(reader);
    ....
    //do things with the feed
    ....
}

Ниже приведен код для SyndicationFeedXmlReader:

public class SyndicationFeedXmlReader : XmlTextReader
{
    readonly string[] Rss20DateTimeHints = { "pubDate" };
    readonly string[] Atom10DateTimeHints = { "updated", "published", "lastBuildDate" };
    private bool isRss2DateTime = false;
    private bool isAtomDateTime = false;

    public SyndicationFeedXmlReader(Stream stream) : base(stream) { }

    public override bool IsStartElement(string localname, string ns)
    {
        isRss2DateTime = false;
        isAtomDateTime = false;

        if (Rss20DateTimeHints.Contains(localname)) isRss2DateTime = true;
        if (Atom10DateTimeHints.Contains(localname)) isAtomDateTime = true;

        return base.IsStartElement(localname, ns);
    }

    public override string ReadString()
    {
        string dateVal = base.ReadString();

        try
        {
            if (isRss2DateTime)
            {
                MethodInfo objMethod = typeof(Rss20FeedFormatter).GetMethod("DateFromString", BindingFlags.NonPublic | BindingFlags.Static);
                Debug.Assert(objMethod != null);
                objMethod.Invoke(null, new object[] { dateVal, this });

            }
            if (isAtomDateTime)
            {
                MethodInfo objMethod = typeof(Atom10FeedFormatter).GetMethod("DateFromString", BindingFlags.NonPublic | BindingFlags.Instance);
                Debug.Assert(objMethod != null);
                objMethod.Invoke(new Atom10FeedFormatter(), new object[] { dateVal, this });
            }
        }
        catch (TargetInvocationException)
        {
            DateTimeFormatInfo dtfi = CultureInfo.CurrentCulture.DateTimeFormat;
            return DateTimeOffset.UtcNow.ToString(dtfi.RFC1123Pattern);
        }

        return dateVal;

    }

}

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

ПРИМЕЧАНИЕ. Один бит настройки, который вам может потребоваться, - это два массива в начале класса. В зависимости от любых посторонних полей, которые может добавить ваш нестандартный канал, вам может потребоваться добавить дополнительные элементы в эти массивы.

Ответ 3

Интересно. Было бы похоже, что форматирование даты и времени не является одним из тех, которые, естественно, ожидаются парсером datetime. Изучив классы фидов, похоже, что вы не можете вставлять в свое собственное соглашение о форматировании для синтаксического анализатора, и они, скорее всего, используют определенную схему проверки подлинности.

Возможно, вы сможете изменить поведение парсера datetime, изменив культуру. Я никогда не делал этого раньше, поэтому не могу сказать, что это сработает.

Еще одна ночь решения - сначала преобразовать корм, который вы пытаетесь прочитать. Вероятно, не самый большой, но он может помочь вам решить проблему.

Удачи.

Ответ 4

Аналогичная проблема сохраняется в .NET 4.0, и я решил работать с XDocument вместо прямого вызова SyndicationFeed. Я описал применяемый метод (специфический для моего проекта здесь). Не могу сказать, что это лучшее решение, но его можно считать "резервным планом" в случае сбоя SyndicationFeed.