使用C#和.net 3.5阅读RSS的问题

时间:2008-10-16 21:27:10

标签: c# rss syndication

我一直在尝试编写一些例程来使用System.ServiceModel.Syndication中提供的新例程来读取RSS和ATOM提要,但不幸的是Rss20FeedFormatter对我尝试的大约一半的提示进行了炸弹,但有以下异常:

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

只要RSS源以下列格式表达发布日期,就会发生这种情况:

  

星期四,2008年10月16日14:23:26 -0700

如果Feed将发布日期表示为GMT,那么情况就好了:

  

星期四,2008年10月16日21:23:26 GMT

如果有一些方法可以使用XMLReaderSettings来解决这个问题,我还没有找到它。有人可以帮忙吗?

4 个答案:

答案 0 :(得分:26)

根据bug report to Microsoft about this中发布的变通方法,我制作了一个专门用于阅读具有非标准日期的SyndicationFeeds的XmlReader。

以下代码与Microsoft网站解决方法中的代码略有不同。使用RFC 1123模式也需要Oppositional's advice

您需要从Stream创建XmlReader,而不是简单地调用XmlReader.Create()。我使用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站点上发布的变通方法中复制的。 ...除了这个适用于我,而在微软发布的那个没有。

注意:您可能需要做的一点定制是在课程开始时的两个数组中。根据非标准Feed可能添加的任何无关字段,您可能需要向这些阵列添加更多项目。

答案 1 :(得分:9)

在序列化 pubDate lastBuildDate 等元素时,RSS 2.0格式的联合供稿会使用RFC 822 date-time specification。遗憾的是,RFC 822日期时间规范是一种非常“灵活”的语法,用于表示DateTime的时区组件。

时区可能以多种方式表示。 “UT”是世界时(以前称为“格林威治标准时间”);允许“GMT”作为对世界时的参考。军事标准对每个区域使用单个字符。 “Z”是世界时。 “A”表示提前一小时,“M”表示提前12小时; “N”是一小时后,“Y”是12小时后。不使用字母“J”。其余两种形式取自ANSI标准X3.51-1975。一个允许明确指示UT的偏移量;另一个使用常见的3字符字符串来表示北美的时区。

我认为该问题涉及如何处理RFC 822日期时间值的区域组件。 Feed格式化程序似乎不处理使用本地差异来指示时区的日期时间。

由于RFC 1123扩展了RFC 822规范,您可以尝试使用DateTimeFormatInfo.RFC1123Pattern(“r”)来处理转换problamatic日期时间,或者为RFC 822格式化日期编写自己的解析代码。另一种选择是使用第三方框架而不是System.ServiceModel.Syndication命名空间类。

似乎有一些known issues具有日期时间解析和Rss20FeedFormatter正在由Microsoft解决。

答案 2 :(得分:2)

有趣。看起来日期时间格式不是日期时间解析器自然期望的格式之一。在查看feed类之后,看起来你不能为解析器注入自己的格式约定,并且它们可能使用特定的方案来验证感觉。

您可以通过修改culture来更改日期时间解析器的行为方式。我之前从未这样做过,所以我不能肯定它会起作用。

另一个解决方案之夜是首先转换您尝试阅读的Feed。可能不是最伟大的,但它可以帮助你解决这个问题。

祝你好运。

答案 3 :(得分:1)

类似的问题仍然存在于.NET 4.0中,我决定使用 XDocument 而不是直接调用 SyndicationFeed 。我描述了应用的方法(特定于我的项目here)。不能说它是最好的解决方案,但如果 SyndicationFeed 失败,它当然可以被视为“备份计划”。