16

我一直在尝试使用 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

如果提要将发布日期表示为 GMT,则一切正常:

格林威治标准时间 10 月 16 日星期四 21:23:26

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

4

4 回答 4

26

根据向 Microsoft 发布的错误报告中发布的解决方法,我制作了一个 XmlReader,专门用于读取具有非标准日期的 SyndicationFeeds。

下面的代码与 Microsoft 网站上的解决方法中的代码略有不同。它还采纳了反对党关于使用 RFC 1123 模式的建议。

您需要从 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 网站上发布的解决方法复制而来的。...除了这个对我有用,而在微软发布的那个没有。

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

于 2010-04-27T20:16:18.177 回答
9

RSS 2.0 格式的联合提要在序列化pubDatelastBuildDate等元素时使用RFC 822 日期时间规范。不幸的是,RFC 822 日期时间规范是一种非常“灵活”的语法,用于表达 DateTime 的时区组件。

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

我相信这个问题涉及如何处理RFC 822 日期时间值的区域组件。提要格式化程序似乎没有处理利用本地差异来指示时区的日期时间。

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

Microsoft 正在解决日期时间解析和 Rss20FeedFormatter的一些已知问题。

于 2008-11-04T19:37:59.237 回答
2

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

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

另一个解决方案是首先转换您要阅读的提要。可能不是最好的,但它可以让你解决这个问题。

祝你好运。

于 2008-10-19T03:06:12.437 回答
1

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

于 2010-09-13T04:23:31.107 回答