Manipuler les flux RSS 2.0 et Atom 1.0 avec le Framework 3.5

Florian Casabianca

Découvrons les nouvelles possibilités en matière de syndication avec le FrameWork 3.5.

N'hésitez pas à commenter cet article ! Commentez ♪

- Titre : Manipuler les flux RSS 2.0 et Atom 1.0 avec le Framework 3.5

- Auteur : Florian Casabianca

- Parution : 22 octobre 2007

- Mise à jour : 22 octobre 2007

- Licence : Les sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentation constitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright ® 2007 Florian Casabianca. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts. Droits de diffusion permanents accordés à Developpez LLC.

Sommaire

- Introduction

- I. Prérequis nécessaires

- II. Consommation d'un flux

- II-A. Chargement d'un flux

- II-B. Lecture des éléments du flux

- III. Création d'un flux

- III-A. Les bases

- III-B. Le template Syndication Service Library

- Conclusion

- Liens

- Remerciements

- Contact

Introduction

Cet article n'a pas pour vocation de vous faire un cours sur la syndication de contenu Web. Il existe déjà de nombreux articles traitant le sujet dont certains sont référencés à la fin de cet article. Nous allons néanmoins faire quelques petits rappels afin de mieux cerner la problématique.

Il existe aujourd'hui différents formats de syndication dont les plus connus sont RSS et Atom. La nouvelle API de syndication du Framework 3.5 prend en charge ces deux formats dans leur version 2.0 pour RSS et 1.0 pour Atom.

Regardons de plus près comment est constitué un flux RSS 2.0 :

rss 2.0

<rss  version="2.0">
<channel >
<title>Titre du flux</title>
<link>http://monflux.com</link>
<description>Ceci est un exemple de flux</description>
<lastBuildDate>Sat, 13 Oct 2007 17:29:38 Z</lastBuildDate>
<category domain="CategoryScheme">Catégorie du flux</category>
<item>
<guid isPermaLink="false">ItemID</guid>
<link>http://monsite/items</link>
<title>Titre de l'item</title>
<description>Contenu de l'item</description>
</item>
</channel>
</rss>

Voyons maintenant un flux Atom 1.0 :

atom 1.0

<feed xml:lang="en-us" xmlns="http://www.w3.org/2005/Atom">
<title type="text">Titre du flux</title>
<subtitle type="text">Exemple de flux Atom 1.0</subtitle>
<id>FeedID</id>
<updated>2007-08-13T17:29:38Z</updated>
<category term="FeedCategory" label="CategoryLabel" scheme="CategoryScheme" />
<logo>http://monsite/image.jpg</logo>
<link rel="alternate" type="text/html" title="Link Title" length="1000" href="http://monsite/link" />
<entry>
<id>ItemID</id>
<title type="text">Titre de l'item</title>
<updated>2007-08-13T17:29:38Z</updated>
<link rel="alternate" href="http://monsite/items" />
<content type="text">Contenu de l'item</content>
</entry>
</feed>

Dans les deux cas, il ne s'agit que des quelques balises de base. Il en existe de nombreuses autres, mais encore une fois vous êtes invités à consulter les liens en fin d'article pour plus d'informations.

Comme vous le voyez, les deux schémas XML diffèrent totalement. Il vous faudra donc deux parseurs XML différents si vous voulez pouvoir lire un flux RSS et un flux Atom. De même, la publication d'un même contenu au travers d'un flux RSS et d'un flux Atom nécessitera deux blocs de codes différents, chacun créant les balises spécifiques pour chaque format.

Ainsi, ne serait-il pas intéressant d'avoir une API à qui l'on fournirait l'adresse d'un flux, qui se chargerait d'en reconnaitre le format (RSS 2.0 ou Atom 1.0), qui effectuerait le parsing nécessaire et qui mapperait le contenu vers des classes .NET facilement utilisables ?

De même, quel gain de temps appréciable serait de pouvoir fournir à cette API simplement le contenu que l'on souhaite voir exposer sous forme de flux, de lui indiquer le format souhaité (RSS 2.0 ou Atom 1.0) et de récupérer en sortie le flux formaté comme il faut.

Un code unique (ou presque) pour gérer deux formats différents, sympa non ? Et bien c'est ce que propose de faire la nouvelle API de syndication du Framework 3.5 : System.ServiceModel.Syndication.

Nous allons illustrer l'utilisation de cette API via une application Web, mais vous pouvez bien entendu vous en servir dans d'autres situations (une application WinForm par exemple).

I - Prérequis nécessaires

Vous aurez donc besoin du Framework 3.5 (encore Béta au moment de l'écriture de cet article) et de Visual Studio 2008 (pas obligatoire, mais conseillé) lui aussi encore en version Bêta.

Pour ajouter une référence à cette dll dans votre projet (sous Visual Studio): dans le menu projet, cliquez sur ajouter une référence. Si la dll System.ServiceModel ne se trouve pas dans l'onglet .Net, choisissez l'onglet Parcourir et allez la chercher dans C:\Program Files\Reference Assemblies\Microsoft\Framework\v3.5.

N'oubliez pas de référencer l'espace de noms System.ServiceModel.Syndication en utilisant la directive suivante :

using System.ServiceModel.Syndication;

II - Consommation d'un flux

II-A - Chargement d'un flux

La classe la plus importante pour la gestion d'un flux se nomme SyndicationFeed. Cette classe représente un flux (RSS 2.0 ou Atom 1.0). Voici un diagramme simplifié de cette classe :

La première chose à faire pour consommer un flux est de créer une instance de SyndicationFeed correspondant à ce flux. Cependant nous n'allons pas utiliser le constructeur, mais la méthode static Load. Il existe trois surcharges de la méthode Load, mais celle qui nous intéresse ici est la version prenant en paramètre un objet de type Uri.

Ainsi, la création d'un objet SyndicationFeed permettant de lire le flux RSS 2.0 du site developpez.com s'effectuera de la façon suivante :

SyndicationFeed feed = SyndicationFeed.Load(new Uri("http://www.developpez.com/rss.php"));

Vous pouvez ensuite accéder à différentes informations concernant le flux :

Response.Write("Titre: " +feed.Title.Text + "<br />");
Response.Write("Description: " + feed.Description.Text + "<br />");
Response.Write("Lien: " + feed.Links.First().Uri.AbsoluteUri + "<br />");

L'affichage correspondant :

Le site developpez.com possède aussi un flux Atom 1.0. Que faudrait-il modifier dans notre code précédent pour qu'il prenne en charge le flux Atom à la place du flux RSS ? Et bien pas grand-chose. Il suffit de modifier la construction de l'objet SyndicationFeed en passant l'adresse du flux Atom 1.0 à la place de celle du flux RSS 2.0 en paramètre de la méthode Load :

SyndicationFeed feed = SyndicationFeed.Load(new Uri("http://www.developpez.com/atom.php"));

Et c'est tout ! Le reste du code reste strictement identique. L'API se charge d'extraire les informations demandées du flux. Par exemple, peu importe que la description du flux se trouve entre des balises description en RSS 2.0 ou entre des balises subtilte en Atom 1.0, l'API fera le parsing nécessaire et exposera le résultat via la propriété Description.

Attention, cette apparente facilité peut cacher quelques problèmes !

Certains sites proposent à la fois un flux RSS 2.0 et un flux Atom 1.0. Si vous produisez du code permettant de lire le flux RSS et que vous tentez de l'utiliser pour lire le flux Atom (ou inversement) vous risquez dans certains cas de vous retrouver avec quelques petites surprises. En effet, il n'est pas rare de voir certaines informations contenues dans le flux RSS ne pas être reportées dans le flux Atom (ou inversement). Vous pouvez donc vous retrouver avec des exceptions du type NullPointerException si vous effectuez des traitements sur des informations qui n'existent plus lors du changement de flux.

II-B - Lecture des éléments du flux

Nous sommes donc maintenant en mesure de charger un flux RSS 2.0 ou Atom 1.0 et d'en lire les différentes propriétés. C'est un bon début, mais nous n'allons pas aller bien loin avec ça. Nous allons donc maintenant nous intéresser à la récupération des différentes alertes, actualités, nouvelles diffusées au travers du flux.

Cette liste d'éléments est accessible via la propriété Itesm qui renvoie une liste (IEnumerable) d'objets de type SyndicationItem dont voici la liste des propriétés :

Cette classe représente donc un élément item dans le cas d'un flux RSS 2.0 et un élément entry dans le cas d'un flux Atom 1.0. Tout ce qui vous reste à faire c'est d'utiliser les propriétés de cet objet pour récupérer les informations voulues.

Voici un premier exemple dans lequel on récupère la liste des dix (au plus) dernières actualités publiées sur le site developpez.com et datant de moins de cinq jours. Pour chaque actualité on ne souhaite récupérer que son titre et sa date de publication et les afficher dans un GridView. Tout cela s'effectue en quelques lignes grâce à LINQ to Object :

var items = from i in feed.Items.Take(10)
where i.PublishDate.Date >= DateTime.Today.AddDays(-5)
select new { Date = i.PublishDate.ToShortDateString(),
Titre = i.Title.Text
};

this.GridView1.DataSource = items;
this.GridView1.DataBind();

Et voici le résultat obtenu :

Facile non ?

III - Création d'un flux

III-A - Les bases

La première chose à faire ici est de créer un objet SyndicationFeed qui contiendra une liste d'objets SyndicationItem. Il existe six versions public du constructeur de SyndicationFeed. La différence se situe simplement au niveau du choix des champs à initialiser directement dans le constructeur.

Nous pouvons par exemple prendre le constructeur suivant qui prend en paramètre le titre du flux, sa description et son adresse :

SyndicationFeed feed = new SyndicationFeed("Mon flux", "Exemple d'un flux de syndication", null);

Il nous faut ensuite créer une liste de SyndicationItem et l'assigner à la propriété Items de l'objet SyndicationFeed que nous venons de construire :

List<SyndicationItem> items = new List<SyndicationItem>();

// Création d'un nouveau Syndication Item.
SyndicationItem item = new SyndicationItem("Un item", "Contenu de l'item", null);
items.Add(item);
// On continue à créer autant d'items que nécessaire

feed.Items = items;

Notre travail est maintenant terminé (ça n'a pas été bien long) et c'est maintenant à l'API de travailler un peu. Nous venons de lui fournir les données à exposer dans un flux de syndication; il ne reste plus qu'à lui demander de créer ce fameux flux.

Une seule instruction suffit pour récupérer un flux RSS 2.0 :

SyndicationFeedFormatter<SyndicationFeed> formatter = new Rss20FeedFormatter(feed);

Vous préférez un flux Atom 1.0 ? Pas de problème :

SyndicationFeedFormatter<SyndicationFeed> formatter = new tom10FeedFormatter(feed);

Facile !

Vous pouvez par exemple récupérer le code XML généré et l'enregistrer dans un fichier :

System.Xml.XmlWriter xmlw = System.Xml.XmlWriter.Create(@"C:\rss.xml");
formatter.WriteTo(xmlw);
xmlw.Close();

Nos flux RSS et Atom sont maintenant créés; reste à les exposer à nos utilisateurs. Pour cela nous allons passer par un service WCF. Ne partez pas si vite, Visual Studio 2008 propose un template WCF spécialement dédié à la création de flux de syndication afin de vous simplifier (c'est peu dire) le travail: Syndication Service Library !

III-B - Le template Syndication Service Library

Créez donc un nouveau projet de type WCF (Framework 3.5) et sélectionnez Syndication Service Library. Le nom de ce template ne laisse aucun doute quant à son utilité.

Une fois le projet créé, son arborescence devrait ressembler à ceci :

L'interface IFeed1 correspond à l'interface définissant opérations exposées par le service :

namespace SyndicationServiceLibrary1
{
// NOTE: If you change the interface name "IFeed1" here, you must also update the reference to "IFeed1" in App.config.
[ServiceContract]
[ServiceKnownType(typeof(Atom10FeedFormatter))]
[ServiceKnownType(typeof(Rss20FeedFormatter))]
public interface IFeed1
{

[OperationContract]
[WebGet(UriTemplate = "*", BodyStyle = WebMessageBodyStyle.Bare)]
SyndicationFeedFormatter<SyndicationFeed> CreateFeed();

// TODO: Add your service operations here
}
}

Elle ne possède qu'une seule méthode renvoyant un SyndicationFeedFormatter. Vous pouvez rajouter d'autres méthodes si vous le souhaitez, mais sa définition de base nous suffit dans notre cas.

La classe Feed1 implémente l'interface IFeed1. C'est dans cette classe que nous allons écrire le code permettant de générer nos flux RSS et Atom. Ce qu'il y a d'intéressant avec le template utilisé, c'est qu'il génère la classe Feed1 avec une implémentation complète et fonctionnelle de l'interface IFeed1. Voyez plutôt (vous devez reconnaitre le code, nous venons juste de le voir) :

namespace SyndicationServiceLibrary1
{
public class Feed1 : IFeed1
{
public SyndicationFeedFormatter<SyndicationFeed> CreateFeed()
{
// Create a new Syndication Feed.
SyndicationFeed feed = new SyndicationFeed("Feed Title", "A WCF Syndication Feed", null);
List<SyndicationItem> items = new List<SyndicationItem>();

// Create a new Syndication Item.
SyndicationItem item = new SyndicationItem("An item", "Item content", null);
items.Add(item);
feed.Items = items;

// Return ATOM or RSS based on query string
// rss -> http://localhost:8080/Feed1/
// atom -> http://localhost:8080/Feed1/?format=atom
string query = WebOperationContext.Current.IncomingRequest.UriTemplateMatch.QueryParameters["format"];
SyndicationFeedFormatter<SyndicationFeed> formatter = null;
if (query == "atom")
{
formatter = new Atom10FeedFormatter(feed);
}
else
{
formatter = new Rss20FeedFormatter(feed);
}

return formatter;
}
}
}

Le code permettant de créer un syndicationFeed et d'en tirer un flux RSS 2.0 et Atom 1.0 est déjà écrit ! Si vous compilez (sans rien toucher) le projet et lancez votre navigateur à l'adresse http://localhost:8080/Feed1/ (ou http://localhost:8080/Feed1/?format=atom pour le flux Atom) vous pourrez visualiser le flux RSS 2.0 d'exemple.

Notez au passage qu'un host pour votre service est créé par Visual Studio lorsque vous lancez un service WCF (un peu à la manière du mini serveur ASP.NET Cassini vous permettant de tester des pages ASP.NET sans avoir à utiliser IIS par exemple) :

N'oubliez pas de configurer le projet de la librairie comme « projet de démarrage ».

Nous construisons un projet de type Syndication Service Library (qui s'apparente à un projet de type WCF Service Library). Il existe deux types de template disponibles dans Visual Studio 2008: WCF Service Libraryet WCF Service Application(disponible dans la partie Web). Une librairie WCF va pouvoir être référencée et « hostée » dans divers types d'applications (WinForm, service Windows, IIS, etc). Cependant c'est à vous de créer ces hosts. Nous n'aborderons pas ici la création de ces hosts ou de projet WCF Service Application. Le host de test créé par Visual Studio suffira amplement pour l'instant.

Tout ce que nous avons à faire est d'ajouter notre propre code pour construire nos flux RSS et Atom. Nous allons par exemple créer un flux exposant la liste des différents processus en cours sur le serveur. Voici la méthode permettant de construire un objet SyndicationFeed correspondant à nos besoins :

public SyndicationFeed GetProcesses()
{
Process[] processes = Process.GetProcesses();
// Nouveau SyndicationFeed.
SyndicationFeed feed = new SyndicationFeed();
feed.Description = SyndicationContent.CreatePlaintextContent("Processus en cours d'execution");
//Titre du flux
feed.Title = SyndicationContent.CreatePlaintextContent("Liste des processus");
feed.Links.Add(SyndicationLink.CreateSelfLink(OperationContext.Current.IncomingMessageHeaders.To));
//Création d'un item RSS/Atom pour chaque processus
feed.Items =
from p in processes
orderby p.ProcessName
select new SyndicationItem
{
Title = SyndicationContent.CreatePlaintextContent(p.ProcessName),
Summary = new TextSyndicationContent(
String.Format("<b>{0}</b>", p.MainWindowTitle), TextSyndicationContentKind.Html)
PublishDate = DateTime.Now
};
return feed;
}

Il suffit ensuite de modifier la fonction CreateFeed générée par le template afin de faire appel à notre fonction GetProcesses :

namespace SyndicationServiceLibrary1
{
public class Feed1 : IFeed1
{
public SyndicationFeedFormatter<SyndicationFeed> CreateFeed()
{
// Create a new Syndication Feed.
SyndicationFeed feed = GetProcesses();

// Return ATOM or RSS based on query string
// rss -> http://localhost:8080/Feed1/
// atom -> http://localhost:8080/Feed1/?format=atom
string query = WebOperationContext.Current.IncomingRequest.UriTemplateMatch.QueryParameters["format"];
SyndicationFeedFormatter<SyndicationFeed> formatter = null;
if (query == "atom")
{
formatter = new Atom10FeedFormatter(feed);
}
else
{
formatter = new Rss20FeedFormatter(feed);
}

return formatter;
}
}
}

Et voilà le travail ! Vous obtenez un flux RSS 2.0 et Atom 1.0 exposant la liste des processus en cours sur la machine. Si nous reprenons le code de lecture de flux que nous avions vu au tout début de cet article et que nous changeons simplement l'adresse du flux à lire, nous obtenons le résultat suivant :

Conclusion

Nous venons de voir comment s'abonner ainsi que créer des flux RSS 2.0 et Atom 1.0. La nouvelle API System.ServiceModel.Syndication rend la gestion de ces flux extrêmement facile. De plus, le template Syndication Service Library associé à WCF va vous permettre de facilement créer des flux de syndication. Vous n'avez donc plus d'excuse pour ne pas consommer ou même proposer des flux de syndication au sein de vos différents projets.

Liens

Blog de Thomas Lebrun

SyndicationFeed sur MSDN

blog d'Alex Thissen

Introduction à Windows Communication Foundation par Ronald VASSEUR

Introduction à Windows Communication Foundation par Lainé Vincent

RSS sur Wikipedia

Atom sur Wikipedia

Remerciements

J'adresse ici tous mes remerciements à l'équipe de rédaction de « developpez.com » pour le temps qu'ils ont bien voulu passer à la correction et à l'amélioration de cet article.

Contact

Si vous constatez une erreur dans le tutoriel, dans les sources, dans la programmation ou pour toutes informations, n'hésitez pas à me contacter par le forum.