30 Janvier 2002
Résumé
La gestion de fichier de type CSV (Comma Separated Values - fichier texte organisé en lignes, une ligne étant décomposée en champs séparés par un caractère délimiteur) est une activité encore courante en informatique de gestion.
Toutefois, le format CSV tend à disparaitre au profit du format XML, plus structuré.
L'étudiant est amené à utiliser différentes techniques :
Première partie
Expression des besoins par un cas d'utilisation (référence à UML) qui sert de fil conducteur à la programmation.
Gestion d'un fichier texte (lecture séquentielle, écriture)
Décomposition fonctionnelle
Utilisation d'un document XSLT (fourni) pour les tests
Deuxième partie - prolongement de la première
Programmation Objet : conception d'une classe de type Helper.
Remarque : les classes désignées Helper sont des classes conçues pour faciliter la conception d'un algorithme. On les trouve fréquement dans des modèles de conception, parfois sous la forme de Value Object, objet qui encapsule des données pour la vue dans des implémentations MVC2 (model view controller adapté aux applications web).
Utilisation d'une collection, avec production de deux implémentations :
une avec une Liste
une autre avec un Dictionnaire
Les "Liste" et "Dictionnaire" sont des types de collection couramment utilisés et font partie des outils de base des langages de programmation objet.
Ce document a été réalisé sous Linux avec gvim, au format docbook , mis en page avec le processeur XSLT saxon développé par Michael Kay et les feuilles de styles de Norman Walsh.
Table des matières
La société qui vous embauche, un grossiste en vêtements, reçoit régulièrement de ses clients des flux d'informations concernant leurs statistiques de ventes.
Ces flux sont constitués de lignes de texte composées ainsi :
<public>;<code produit>;<quantité vendue>;<mois>;<année>;<commentaire> où <public> est soit 'F' (femme), 'H' (homme), 'M' (mixte) <code produit> le code fournisseur du produit <quantité vendue> la quantité vendue pour le mois <mois> mois concerné par les statistiques de vente <année> année concernée par les statistiques de vente <commentaire> un commentaire libre et optionnel |
Dans le cas présent, on nous assure que le caractère séparateur ';' est présent dans le fichier uniquement comme séparateur de champs (il n'y en a pas dans la partie commentaire) et que le contenu de la partie commentaire n'est pas encadré par des guillemets, comme c'est le cas d'ordinaire avec le format CSV lorsqu'une donnée contient des espaces.
Exemple :
F;a12;21;mars;2002; Attention, léger défaut de fabrication sur la ceinture. |
Cette chaîne signifie que le produit, catalogué pour "femme" et de code "a12", a fait l'objet de "21" ventes au mois de "mars" "2002". Le détaillant signale un léger défaut de fabrication.
La direction vous demande de créer un programme qui traduit un fichier de données au format CSV (Comma Separated Values) transmis par un cient (comme indiqué ci-dessus), en un fichier XML (eXtensible Markup Language). Pour cela, vous devez respecter le jeu de balises fourni par l'exemple ci-dessous.
Exemple de données source CSV :
F;a12;21;mars;2002; Attention, léger défaut de fabrication sur la ceinture. H;d45;11;mai;2002; F;a12;6;mars;2002; |
Exemple de données résultat XML :
<?xml version="1.0" encoding="ISO-8859-1"?> <ventes> <produit> <public>F</public> <code>a12</code> <qte>21</qte> <mois>mars</mois> <annee>2002</annee> <commentaire>Attention, léger défaut de fabrication sur la ceinture.</commentaire> </produit> <produit> <public>H</public> <code>d45</code> <qte>11</qte> <mois>mai</mois> <annee>2002</annee> </produit> <produit> <public>F</public> <code>a12</code> <qte>6</qte> <mois>mars</mois> <annee>2002</annee> </produit> </ventes> |
Afin de vérifier la conformité du document xml produit par votre programme, Bob, le développeur spécialisé dans la représentation des données, vous fournit la feuille de style ventes.xsl (voir annexe), qui permet de traduire un fichier xml de ventes en un fichier HTML.
On utilisera le programme xalan dans le but d'appliquer les règles de transformation de ventes.xsl sur le document résultat du traitement de votre programme. Supposons que votre programme produise le fichier ventes.xml, nous appelons le processeur xalan de la façon suivante, dans le but de produire le fichier ventes.html :
java org.apache.xalan.xslt.Process -in ventes.xml -xsl ventes.xsl -out ventes.html |
Voici le résultat :
L'objectif de cette séance n'est pas l'étude de XSLT (Extensible Stylesheet Language Transformation). Toutefois une note d'introduction est disponible en annexe.
![]() | Si vous programmez en Java |
Le jdk 1.4 inclut d'office xalan et les packages nécessaires à son exécution. Vous pouvez aussi télécharger les packages Xalan-Java 2 sur le site de la fondation Apache : http://xml.apache.org . |
![]() | Cas d'utilisation : ConvertirVentesCSVversXML |
Scénario typique
Cas particuliers
|
Vous utiliserez ce fichier de données pour les tests : ventes.csv.
Votre application devra prendre en compte les cas particuliers du cas d'utilisation.
Conseil : Traiter les cas particuliers dans un second temps. Concentrez vous en premier sur le cas nominal (le cas typique).
Bravo, votre programme fonctionne correctement, mais doit évoluer afin de répondre au problème suivant.
Le service des statisiques a demandé à Bob de cumuler les quantités concernant un même produit pour une même période.
En effet, certaines lignes du document CSV concernent le même produit, pour un même mois. Dans l'exemple, repris ci-dessous :
1: F;a12;21;mars;2002; Attention, léger défaut de fabrication sur la ceinture. 2: H;d45;11;mai;2002; 3: F;a12;6;mars;2002; |
Les lignes 1 et 3 concernent le produit a12 pour le mois de mars 2002. Bob souhaiterait que, dans ce cas, les quantités soient cumulées afin de limiter à une instance tout produit (code/mois) dans le document XML (de cette façon il n'aura rien à changer aux fichiers XSLT dont il a la charge, le malin...). Exemple :
<?xml version="1.0" encoding="ISO-8859-1"?> <ventes> <produit> <public>F</public> <code>a12</code> <qte>27</qte> <mois>mars</mois> <annee>2002</annee> <commentaire>Attention, léger défaut de fabrication sur la ceinture.</commentaire> </produit> <produit> <public>H</public> <code>d45</code> <qte>11</qte> <mois>mai</mois> <annee>2002</annee> <commentaire/> </produit> </ventes> |
Une méthode possible est de créer une liste de produits/mois en mémoire. Chaque produit/mois est identifié par son code, le mois et l'année. Pour chaque ligne du fichier CSV il faut vérifier si le produit/mois existe ou non dans la liste (parcours séquentiel) :
si le produit/mois existe dans la liste (même code, mois et année), il faut mettre à jour le produit/mois existant
si le produit/mois n'existe pas, il faut l'ajouter à la liste
Pour cela vous
Concevrez une classe ProduitMois, caractérisant un produit (on retrouvera les 6 attributs) et disposant des seules méthodes nécessaires à la résolution de ce problème.
A chaque lecture d'une ligne du fichier csv, un objet ProduitMois est créé.
Vous placerez cet objet dans une liste (par exemple Vector ou ArrayList), attribut de votre classe responsable du traitement principal.
Toutefois, si un objet ayant le même code, même mois et même année est déjà présent dans la liste, celui ci sera mis à jour, et vous n'insérerez pas dans la liste l'objet nouvellement créé.
A la fin, vous inscrirez l'image XML de chaque produit/mois de la liste dans le fichier de sortie. C'est l'objet de type ProduitMois qui sera responsable de sa représentation au format XML. Prévoir une méthode pour cela.
Bien entendu, on veillera à ce qu'aucune information figurant dans la source csv ne soit perdue par cette nouvelle version de l'application... Soyez vigilent quant au résultat obtenu.
Vous proposerez deux implémentations :
Avec une liste, conformément aux indications ci-dessus (par exemple Vector ou ArrayList), et d'une fonction de recherche séquentielle ayant l'interface suivante :
// rend l'indice du premier produit de la liste ayant une clé égale // à la valeur de l'argument nommé uneCle. // rend -1 si non présent dans la liste // remarque1 : la liste de ProduitMois sera représentée par une variable d'instance // remarque2 : on entend par clé une concaténation d'attributs qui identifie un produit privé fonction rechProduitDansListe(uneCle : chaîne) : entier { // A FAIRE } |
Avec un dictionnaire (Par exemple vous pouvez utiliser un objet de la classe TreeMap qui implémente l'interface Map).
Nous pouvons généraliser notre travail.
Imaginons que les clients nous retournent le fichier de données sous cette forme :
public;code;qte;mois;annee;commentaire F;a12;21;mars;2002;"Attention, ""léger"" défaut de fabrication sur la ceinture." H;d45;11;mai;2002; F;a12;6;mars;2002; |
Vous remarquerez que :
La première ligne du fichier de données décrit les différentes balises, dans le bon ordre.
Le contenu de la partie commentaire est encadré de guillements (").
Lorsqu'un guillemet ne joue pas le rôle de délimiteur de contenu, il est alors systématiquement doublé.
Après avoir sauvegardé la dernière version de votre application et analysé le problème, vous proposerez une solution qui prenne en compte les 3 caractéristiques décrites ci-dessus.
Conseil : analysez et concevez par itérations successives. Prenez en compte dans un premier temps uniquement le problème des guillemets, en sautant, lors de la lecture, la première ligne du fichier (qui contient les noms des balises).
Un document au format XML est un document texte constitué de balises. Mais contrairement au langage HTML, il est possible de définir librement les balises utilisées. En se focalisant essentiellement sur la structure du document (par un jeu de balises) et le contenu (à l'intérieur des balises), XML se présente comme un modèle d'architecture dans lequel les données sont séparées de la logique de présentation.
Structure minimale d'un document XML
Un en-tête <?xml version="1.0"?>, nommé prologue, auquel on peut ajouter des valeurs d'attributs non présentés ici.
Un élément racine (un unique couple de balises, une ouvrante et une fermante).
Un arbre d'éléments.
D'éventuels commentaires.
Exemple d'un document XML (source : sujet d'examen BTS IG 2002) :
<?xml version="1.0"?> <!-- ceci est un commentaire --> <article> <auteur> Boby Lapointe </auteur> <copyright/> <titre>Une introduction à XML</titre> <section> <titre>Présentation de XML</titre> <paragraphe>Ce document est bien formé</paragraphe> </section> </article> |
Lors de traitements, un document XML est chargé en mémoire sous la forme d'un arbre. Exemple.
Ce document XML est bien formé car
Toutes ses balises ouvrantes ont une balise fermante associée.
Mal formé : <a> <b> <c> </c> </a>
Bien formé : <a> <b> <c> </c> </b> </a>
Remarque : un élément vide peut s'écrire <copyright></copyright> ou simplement <copyright />.
La balise de fin d'un élément imbriqué est placée avant la balise de fin de l'élément conteneur et il n'y a aucun chevauchement entre balises de même niveau.
Mal formé : <a> <b> <c> </b> </c> </a>
Bien formé : <a> <b> <c> </c> </b> </a> ou <a> <b> </b> <c> </c> </a>
De plus, une balise peut contenir des attributs. Un attribut est constitué d'une paire nom=valeur, le nom étant en minuscule et sa valeur entre guillemets (double quote). Le document peut faire référence à des entités internes et externes, contenir des instructions de traitement, définir des espaces de nom. Exemple.
<?xml version="1.0" encoding="ISO-8859-1"?> <?xml-stylesheet type="text/xsl" href="ventes.xsl"?> <ventes mois="mars"> <produit> <public>F</public> <code>a12</code> <qte>21</qte> <annee>2002</annee> <commentaire>Attention, léger défaut de fabrication sur la ceinture.</commentaire> </produit> <produit> <public>F</public> <code>a12</code> <qte>6</qte> <annee>2002</annee> <commentaire></commentaire> </produit> </ventes> |
Certains navigateurs, équipés d'un processeur XSLT, peuvent interpréter le document XML par le biais d'un document XSLT mentionné dans une instruction de traitement, comme c'est le cas dans cet exemple.
Les documents XML peuvent être classés en deux catégories :
Document XML régulier : La structure est une répétition d'un élément type comme par exemple la représentation XML d'une table de base de données (chaque élément correspondant à un tuple de la table).
Document XML non régulier : La structure est de type arbitraire, les éléments ne sont pas tous formés sur le même moule (nature des balises et profondeur). Par exemple la représentation XML d'un livre respectant le jeu de balises DocBook.
Enfin, lorsque plusieurs documents partagent le même jeu de balises, celui-ci est généralement décrit par une DTD (Document Type Definition) ou, plus récemment, par un XMLSchema (avec définition des types de données).
Le langage XSLT est défini par le World Wide Web Consortium (W3C), et la version 1.0 de ce langage a été publiée en tant que Recommandation le 16 Novembre 1999.
Le principal objectif de XSLT est la séparation du contenu de l'information de sa présentation, principalement en vue d'une exploitation sur le Web. Ainsi, pour un même contenu (document XML), on peut proposer plusieurs présentations (documents XSLT).
Un document XSLT est un document XML. Il commence donc par une déclaration XML standard (<?xml version"=1.0">) suivi de <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> et se termine bien entendu par </xsl:stylesheet>.
Le "corps" du document XSLT est en gros constitué de deux types d'éléments : les instructions et les littéraux. Une instruction XSLT décrit comment un noeud, sous-arbre du document XML sur lequel elle travaille, sera transformé. Un littéral est du texte qui sera copié tel quel dans le document résultat, ce texte peut correspondre à du XML, HTML ou du simple texte. Exemple.
1: <xsl:template match="/"> 2: <html><head></head><body><center><br/> 3: <h1>VENTES</h1> 4: <xsl:apply-templates select="ventes" /> 5: </center></body></html> 6: </xsl:template> |
Ligne 1 : Une entête de définition d'une instruction de transformation template. L'attribut match="/" désigne le type des noeuds devant être traités par cette instruction. La syntaxe utilisée pour désigner un noeud de l'arbre du document source XML est définie par la norme XPath, une recommandation du W3C.
Lignes 2, 3, 5 : Des éléments littéraux, ici du HTML, qui seront donc placés dans le document résultat, chaque fois qu'un noeud "/" sera rencontré, c'est à dire une seule fois, car le noeud "/" est le noeud racine du document source XML.
Ligne 4 : Une instruction qui sera exécutée individuellement sur chacun des enfants du noeud courant (ici la racine) étiqueté "ventes". Si une instruction de type <xsl:template match="ventes"> est présente dans le document XSLT, alors elle est exécutée, sinon le texte compris entre les balises <ventes> et </ventes> est simplement recopié dans le document résultat.
Michael Kay (auteur du processeur XSLT saxon écrit en Java) n'hésite pas à dire que "XSLT est au document XML ce que SQL est aux données tabulaires". Ce domaine technique intéresse de plus en plus d'entreprises, de nombreux ouvrages et sites web traitent ce sujet, n'hésitez pas à vous y investir.
Pour la publication de données sur un site web (ou wap ou autres), il est utile de séparer le contenu de la présentation.
Le contenu est représenté par un document XML.
La présentation est décrite par un document XSLT, composé d'ordres de transformation pour chacun des noeuds d'un arbre de document XML respectant un jeu de balises dont il a connaissance.
La transformation est opérée grace au processeur XSLT.
source : Michael Kay
Exemple.
java org.apache.xalan.xslt.Process -in ventes.xml -xsl ventes.xsl -out ventes.html |
Dans cet exemple, xalan est le XSLT processor, ventes.xml le Source document transformé en arbre, ventes.xsl le Stylesheet et ventes.html le Result document produit par le processeur.
Voici le contenu du fichier ventes.xsl transmis par Bob.
<?xml version="1.0" encoding="ISO-8859-1"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output method="html" omit-xml-declaration="no" indent="yes"/> <!-- **************************************************************************** ** root template *****************************************************************************--> <xsl:template match="/"> <html><head></head><body><center><br/> <h1>VENTES</h1> <xsl:apply-templates select="ventes" /> </center></body></html> </xsl:template> <!-- **************************************************************************** ** "ventes" template *****************************************************************************--> <xsl:template match="ventes"> <table border="1"> <th>code</th><th>public</th><th>mois</th> <th>annee</th><th>qte</th><th>commentaire</th> <xsl:for-each select="produit"> <tr> <td><xsl:apply-templates select="code"/></td> <td><xsl:apply-templates select="public"/></td> <td><xsl:apply-templates select="mois"/></td> <td><xsl:apply-templates select="annee"/></td> <td><xsl:apply-templates select="qte"/></td> <td><xsl:apply-templates select="commentaire"/></td> </tr> </xsl:for-each> </table> </xsl:template> <xsl:template match="commentaire"> <xsl:value-of select = "."/> <xsl:text disable-output-escaping="yes">&nbsp;</xsl:text> </xsl:template> </xsl:stylesheet> |
![]() | Remarque | |
En absence d'instructions de transformation d'un élément, comme c'est le cas dans ventes.xsl pour les éléments code, public, mois, annee, qte, son contenu est simplement copié dans le document résultat. Ceci nous convient bien, sauf pour l'élément commentaire pouvant parfois être vide. Or, comme celui-ci est placé dans une cellule de tableau, nous devons insérer au moins un espace (&nbps; pour non breaking space) afin d'obtenir un résultat HTML satisfaisant. C'est pourquoi l'instruction de ce noeud est explicitement défini par :
|