2008-10-16 16 views
16

He estado intentando escribir algunas rutinas para leer feeds RSS y ATOM usando las nuevas rutinas disponibles en System.ServiceModel.Syndication, pero desafortunadamente el Rss20FeedFormatter se dispara en aproximadamente la mitad de las fuentes que yo tratar con la siguiente excepción:Problemas al leer RSS con C# y .net 3.5

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

Esto parece ocurrir cada vez que el feed RSS expresa la fecha de publicación en el siguiente formato:

Jue 16 Oct 08 14: 2 03:26 -0700

Si la alimentación se expresa como la fecha de publicación GMT, las cosas van bien:

Jue 16 Oct 08 21:23:26 GMT

Si hay alguna forma de evitar esto con XMLReaderSettings, no lo he encontrado. ¿Alguien puede ayudar?

+0

Los incorporados son horribles. Puede escribir fácilmente sus propios analizadores RSS, RDF y ATOM. Tengo un tutorial y un proyecto completo de estudio visual que puedes descargar y que solo hace eso http://www.jarloo.com/rumormill-5/ – Kelly

Respuesta

9

RSS 2.0 sindicación formateado alimenta utilizar los RFC 822 date-time specification elementos al serializar como pubDate y lastBuildDate. La especificación de fecha y hora de RFC 822 es lamentablemente una sintaxis muy flexible para expresar el componente de zona horaria de DateTime.

La zona horaria se puede indicar de varias maneras. "UT" es Universal Time (anteriormente llamado "Greenwich Mean Time"); "GMT" está permitido como referencia al tiempo universal. El estándar militar usa un solo carácter para cada zona. "Z" es el tiempo universal. "A" indica una hora antes, y "M" indica 12 horas antes; "N" es una hora más tarde, e "Y" es 12 horas después. La letra "J" no se usa. Los otros dos formularios restantes están tomados del estándar ANSI X3.51-1975. Uno permite una indicación explícita de la cantidad de compensación de UT; el otro usa cadenas comunes de 3 caracteres para indicar zonas horarias en América del Norte.

Creo que el problema consiste en cómo se está procesando el componente zona del valor RFC 822 de fecha y hora. Parece que el formateador de alimentación no maneja los tiempos de fecha que utilizan un diferencial local para indicar la zona horaria.

Como RFC 1123 amplía la especificación RFC 822, puede intentar usar el DateTimeFormatInfo.RFC1123Pattern ("r") para manejar la conversión de tiempos de fecha problamatic o escribir su propio código de análisis para las fechas formateadas RFC 822.Otra opción sería utilizar un marco de terceros en lugar de las clases de espacio de nombres System.ServiceModel.Syndication.

Parece que hay algunos known issues con el análisis de fecha y hora y el Rss20FeedFormatter que están en proceso de ser tratados por Microsoft.

+1

Gracias - parece que esto fue traído a la atención de Microsoft, fue en febrero pero no está arreglado todavía. :( – dan90266

2

Interesante. Parece que el formateo de fecha y hora no es uno de los esperados naturalmente por el analizador de fecha y hora. Después de mirar las clases de fuentes, no parece que pueda insertarlas en su propia convención de formato para el analizador y es probable que usen un esquema específico para validar la sensación.

Puede cambiar cómo se comporta el analizador de fecha y hora modificando el culture. Nunca lo había hecho antes, así que no puedo decir con certeza que funcionaría.

Otra solución nocturna es transformar primero el feed que está tratando de leer. Probablemente no sea el mejor, pero podría ayudarlo a resolver el problema.

Buena suerte.

26

Basado en la solución publicada en el bug report to Microsoft about this Hice un XmlReader específicamente para leer SyndicationFeeds que tienen fechas no estándar.

El código siguiente es ligeramente diferente del código en la solución en el sitio de Microsoft. También toma Oppositional's advice usando el patrón RFC 1123.

En lugar de simplemente llamar a XmlReader.Create(), necesita crear el XmlReader desde un Stream. Yo uso la clase WebClient para conseguir que la corriente:

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

A continuación se muestra el código de la 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; 

    } 

} 

De nuevo, esto se copia casi exacta de la solución publicada en el sitio de Microsoft en el enlace encima. ... excepto que este funciona para mí, y el publicado en Microsoft no.

NOTA: Un poco de personalización que puede necesitar hacer es en las dos matrices al inicio de la clase. Dependiendo de cualquier campo extraño que pueda agregar su feed no estándar, es posible que necesite agregar más elementos a esas matrices.

+0

Parece que se da por vencido de poder utilizar XmlReaderSettings con este método, es decir, la opción DtdProcessing. Un problema para los canales que aún hace referencia al rss-0.91.dtd. – Ant

+4

me funcionó, pero agregué un DateTime.Parse (dateVal) en el catch. No hay razón para tirar un datetime perfectamente bueno solo porque no está en el formato que los formateadores integrados quieren. Esta es mi última clase: https://gist.github.com/jaminto/4958435 – jaminto

1

Un problema similar aún persiste en .NET 4.0 y decidí trabajar con XDocument en lugar de invocar directamente SyndicationFeed. Describí el método aplicado (específico para mi proyecto here). No puedo decir que sea la mejor solución, pero ciertamente puede considerarse un "plan de respaldo" en caso de que SyndicationFeed falle.