Проблемы чтения RSS с C # и .net 3.5 - PullRequest
16 голосов
/ 17 октября 2008

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

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

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

Чт, 16 октября, 08:23:26 -0700

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

Четверг, 16 октября 08 21:23:26 GMT

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

Ответы [ 4 ]

26 голосов
/ 28 апреля 2010

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

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

Вместо простого вызова XmlReader.Create () вам необходимо создать XmlReader из потока. Я использую класс 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, не.

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

9 голосов
/ 04 ноября 2008

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

Часовой пояс можно указывать несколькими способами. «UT» - универсальное время (ранее называемое «средним временем по Гринвичу»); «GMT» разрешено как ссылка на Всемирное время. Военный стандарт использует один символ для каждой зоны. «Z» - универсальное время. «А» указывает на один час раньше, а «М» указывает на 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 голосов
/ 19 октября 2008

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

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

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

Удачи.

1 голос
/ 13 сентября 2010

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

...