I. Introduction

XML par ci, XML par là, le futur est parmi nous... Le moins que l'on puisse dire, c'est que XML fait tapage dans le monde de l'informatique et qu'il fait bonne figure sur un CV.

Beaucoup de développements tournent aujourd'hui autour du web, des assistants de poche, des WAP et autres, il devenait nécessaire de définir une norme universelle de description des données, quelque soit la plate-forme cible. XML répond à cette exigence, en théorie du moins, car le phénomène a encore du chemin à parcourir sur la voie de la standardisation, les différents éditeurs intéressés n'ayant pas l'habitude de s'amouracher sur la question... :-)

II. XML ?

L'objectif n'est pas de refaire un cours sur XML d'autant que vous trouverez de quoi apprendre sur  xml.developpez.com . Mais il est toujours utile de proposer quelques rappels succins.

XML n'a rien de révolutionnaire. XML est un langage qui permet de structurer des données à l'aide d'une norme de balisage, à l'instar du HTML. Cela permet aux applicatifs d'échanger des données, "formatées" XML. N'importe quel développeur peut en faire autant ceci dit, nul besoin de XML; il suffit de convenir d'un format entre le client et le serveur et d'interpréter en fonction.

L'intérêt d'XML réside plutôt dans l'existence de bibliothèques qui implémentent les API standards d'XML (DOM, SAX etc.) et ce sur de nombreuses plate-formes. Même une technologie propriétaire comme Flash (de Macromédia) embarque un parseur XML. Du coup, il devient réellement intéressant de concevoir ses paquets de données au format XML, lorsqu'elles doivent être communiquées à un autre service ou système. Par exemple il était naturel pour tout delphinautes d'utiliser les classes de Inifiles.pas pour gérer et structurer les fichiers *.ini. Nous allons garder le concept mais "version Starwars", ..., avec XML. En général on extrait les données d'une base SQL, on les maquille XML et on les transmet. Mais attention, XML n'a pas vocation et ne peut pas remplacer une base SQL !!!

Avant de continuer, il est préférable que vous connaissiez les rudiments d'XML, au moins le vocabulaire de base ("racine", "élément", "attribut", "nom" etc.).

Rappel :  xml.developpez.com

III. XML et Delphi 6

Image non disponible

Nous verrons dans cet article comment créer, lire et parcourir un fichier XML grâce aux nouveaux composants et experts du nouveau né  Delphi 6  (beau bébé !). Alors pour quel usage ? L'objectif est de créer un paquet de données organisées dans un format souple et reconnu par un maximum de plateformes (qui se chargeront ensuite de la mise en forme de ces données grâce à une lecture assistée via les bibliothèques XML des différents langages). XML est très souvent employé dans un mécanisme d'échange : les données pouvant être stockées physiquement dans un SGBD traditionnel, et converties en XML juste pour la transmission.

Exemples :

Comme cité plus haut, on pourrait comparer un fichier INI à un fichier XML dans le cadre du stockage d'information : Un fichier INI comporte des sections et des couples chaîne=valeur :

 
Sélectionnez
  1. [SOCIETE] 
  2. NOM=4CENT AGENCY 
  3. [CONTACT]  
  4. NOM=JAMES Sylvain 
  5. ADRESSE=LA-BAS 
  6. EMAIL=sylvain.james@wanadoo.fr 

Un fichier XML comporte des noeuds donc présente une structure arborescente, l'arborescence étant présentée par des indentations de lignes :

 
Sélectionnez
  1. <Sylvain>  
  2. <Societe>4CENT AGENCY</Societe>  
  3. <Contact Nom="JAMES Sylvain" Adresse="LA-BAS" email="sylvain.james@wanadoo.fr"/> </Sylvain> 

Un fichier XML conviendrait (pourquoi pas ?) pour stocker le registre de windows voire une image de l'explorateur de fichiers.

Sur le web, les fichiers XML sont très utilisés comme des conteneurs d'informations (Météo, News, Programmes télé, catalogues etc.) sur lesquels viennent puiser quantité d'autres sites, qui ensuite ajoutent une couche de présentation (dans ce contexte, voir aussi du côté de SOAP).

On pourrait également utiliser la norme XML pour représenter le résultat d'une requête SQL (ce que font déjà certains SGBD comme Oracle ou SQLServer). Imaginons deux tables en relation maître détail, des participants à un stage par exemple.

Image non disponible

Une requête comme :

 
Sélectionnez
  1. SELECT stages.NOMSTAGE, stages.DATESTAGE, stagiaires.NOMSTAGIAIRE, stagiaires.AGESTAGIAIRE  
  2. FROM stages,stagiaires 
  3.  WHERE stages.IDSTAGE = stagiaires.IDSTAGE 

va retourner les données sous la forme suivante :

Image non disponible

La représentation XML du même jeu de données sera la suivante :

 
Sélectionnez
  1. <Stages>  
  2. <Stage Nom="WORD 2000 Initiation" Date="14/06/2001">  
  3. <Stagiaires>  
  4. <Stagiaire Nom="MAGIC Jordan" Age="35"/>  
  5. <Stagiaire Nom="ESSONCHA Jean-Roland" Age="38"/>  
  6. </Stagiaires>  
  7. </Stage>  
  8. <Stage Nom="SQL SERVER 7 Administration" Date="11/06/2001">  
  9. <Stagiaires>  
  10. <Stagiaire Nom="DERIEN Pepito" Age="26"/> </Stagiaires>  
  11. </Stage>  
  12. <Stage Nom="DELPHI 5 Perfectionnement" Date="01/06/2001">  
  13. <Stagiaires>  
  14. <Stagiaire Nom="SUSHI Okimi" Age="42"/>  
  15. </Stagiaires>  
  16. </Stage>  
  17. </Stages> 

On n'ira pas dire qu'il y a économie d'espace avec le XML, mais la présentation des données est radicalement différente. Dans le cas de la requête SQL, la description des champs est fixe mais les données se répètent pour chaque occurrence (NOMSTAGE, DATESTAGE).

Dans la représentation XML, les données ne se répètent pas, mais sont encadrées par leur description ce qui paraît plus souple. XML présente des données de façon arborescente en décrivant chaque élément et éventuellement ses attributs. Un élément représente une entité, comme <stagiaire> par exemple. Un attribut est comme une propriété d'un élément, « Age » est un attribut de <stagiaire>.

Il faut dire que le SQL n'est pas adapté lorsqu'il s'agit de définir des requêtes portant sur des données arborescentes. XML peut prendre le relais dans ces cas là tout en gardant à l'esprit qu'en termes de performances, XML se révèrela nettement inférieur à un SGBD (ne succombez pas à la comparaison d'ailleurs, il n'y a pas lieu...).

Delphi 6 propose des outils pour "mapper" des données issues de Datasets (ClientDataSet, Query, Table ....) vers le format XML. Nous verrons cela dans un prochain topo, ici il s'agit de savoir comment construire un document XML à la mimine :

Image non disponible

IV. Un début à tout

J usque là, pas de quoi encenser XML. Mais XML et ses frangins comportent aussi des outils qui offrent la possibilité de lire et naviguer dans un document XML, l'intérêt est là. Si vous avez déjà entendu parler des parsers DOM et SAX, alors vous brûlez.

Ces parsers sont des outils implémentant des API standardisées (et/ou en cours de ...) qui offrent des interfaces permettant de parcourir, de rechercher et d'éditer facilement une information au sein d'un document XML. Nous nous appuieront sur l'API standard DOM (Document Object Model) via l'implémentation de Microsoft dans msxml.dll (cette lib doit être enregistrée comme serveur COM au moyen de Regsvr32.exe) si elle ne l'est pas encore (rare car installé avec IE).

C'est l'unité  XMLDOM.pas  qui décrit les interfaces implémentées dans les librairies DOM selon les spécifications du W3C. Pour nous le point de départ se situera avec le composant  XMLDocument  : palette de composants, onglet Internet :

Image non disponible

Posez un composant XMLDocument sur une form vierge. Nommez le XMLDoc (propriété Name). Sans rentrer dans les détails, paramétrez les propriétés de ce petit nouveau selon le screenshot suivant :

Image non disponible

Les propriétés essentielles :

  • DOMVendor  qui permet de spécifier la librairie responsable de l'implémentation DOM

et si vous voulez travailler avec un document XML existant :

  • XML  qui est une liste de chaînes stockant le document XML. A savoir que l'on n'est pas obligé d'affecter une liste de chaîne à la propriété XML, on peut également assigner un nom de fichier à la propriété FileName.
  • Active  qui lorsqu'elle est à True permet au composant XMLDocument de créer et/ou parcourir l'arborescence des noeuds du document XML respectif. Tout document XML commence par <?xml version="1.0" encoding="UTF-8"?> l'encodage pouvant varier. Le simple fait de passer la propriété Active à true sur une document XML vierge, va ajouter cette ligne descriptive.

V. Créer la structure XML

Le composant XMLDocument comporte une propriété  DocumentElement  de type  IXMLNode  comme chaque noeud de l'arborescence. DocumentElement a la particularité cependant d'être la racine (le root) de l'arborescence. Si on reprend notre exemple de document XML sur les Stages / Stagiaires, le root correspond au noeud <stages>. Le document doit comporter obligatoirement un root.

Nous allons créer un noeud (IXMLNode) et l'affecter à la propriété DocumentElement. Ce noeud sera le point de départ, la référence à partir de laquelle nous allons créer les autres noeuds, les noeuds enfants.

Rappel technique
IXMLNode est une classe d'interface et non une classe d'objet. A ce titre, sa fonction est de présenter une liste de méthodes mais sans leur implémentation. Cette charge revient aux classes gérant les interfaces (le plus souvent hériées de TInterfacedObject). Une même classe peut implémenter plusieurs interfaces. Les classes XML n'échappent pas à la règle.

Par exemple la classe TXMLNode implémente - entre autres - l'interface IXMLNode. :

TXMLNode = class(TInterfacedObject, IXMLNode, IXMLNodeAccess)

Une variable peut être déclarée de type interface (comme IXMLNode) mais doit être instanciée avec une classe objet implémentant cette interface (comme la classe TXMLNode).

Placez un bouton et un memo sur la form, nommez le btnCreation et implémentez son gestionnaire OnClick de la façon suivante :

 
Sélectionnez
  1. procedure TForm1.btnCreationClick(Sender: TObject); var stage,stagiaires,stagiaire : IXMLNode; //Noeuds de référence begin memo1.Clear; //Création du premier noeud 'stages' et initialisation de DocumentElement XMLDoc.DocumentElement := XMLDoc.CreateElement('stages',''); //Création du premier noeud enfant, représentant le premier stage stage := XMLDoc.DocumentElement.AddChild('stage'); //Création des attributs de ce stage stage.Attributes['nom'] := 'WORD 2000 Initiation'; stage.Attributes['date'] := '14/06/2001'; //Création du noeud enfant de ce stage : stagaires stagiaires := stage.AddChild('stagiaires'); //Création de chaque stagiaire stagiaire := stagiaires.AddChild('stagiaire'); stagiaire.Attributes['nom'] := 'MAGIC Jordan'; stagiaire.Attributes['age'] := '35'; //2è stagiaire stagiaire := stagiaires.AddChild('stagiaire'); stagiaire.Attributes['nom'] := 'ESSONCHA Jean-Roland'; stagiaire.Attributes['age'] := '38'; //Stage suivant stage := XMLDoc.DocumentElement.AddChild('stage'); //Création des attributs de ce stage stage.Attributes['nom'] := 'SQL SERVER 7 Administration'; stage.Attributes['date'] := '11/06/2001'; //et ainsi de suite //...à la fin on peut visualiser le résultat ! Simplissimo :-) memo1.Lines.Assign(XMLDoc.XML); end; 

A cette étape, avec 2 méthodes et une propriété, nous avons créé un document XML représentant une liste hiérarchisées de stages et leurs stagiaires respectifs. Il est aisé de compléter le code ci-dessus avec une interface proposant des champs d'édition et des boutons pour ajouter un stage ou un stagiaire.

  • La fonction CreateElement renvoie le noeud de type IXMLNode qui est créé.
  • La fonction AddChild retourne également le noeud IXMLNode qui est créé, mais il est crée à partir d'un autre noeud et non du document. Le noeud créé devient enfant du noeud qui a appelé cette fonction.
  • La propriété Attributes permet de "mapper" un mot clé et de lui attribuer une valeur. Attributes ici s'applique à un noeud désigné.

Pour terminer cette première partie, enregistrez ce document XML dans le dossier de votre choix en appelant la méthode SaveToFile de l'objet XMLDocument :

 
Sélectionnez
  1. XMLDoc.SaveToFile('stages.xml'); 

Pour un premier aperçu, vous pouvez relire ce document XML à l'aide de votre navigateur Web, vous verrez que ce dernier sait "présenter" le document avec une arborescence conforme à votre description.

VI. L'expert Liaison de données

Continuons notre exploration XML et Delphi 6 et voyons à présent l'expert [ Liaison de données XML ] et la navigation dans une structure XML :

Nous souhaitons lire un fichier XML existant. Delphi 6 propose un  expert saisissant  qui analyse la structure d'un document XML et crée des types interface qui "mappent" les éléments et attributs du document. Il suffit ensuite d'inclure la déclaration de ces types dans vos unités Delphi, et la magie opère...

Exemple, cliquez sur l'icône Nouveau et sélectionnez l'icône XML Data Binding :

Image non disponible

Cet expert va ensuite vous demander quel fichier XML il doit analyser; modifiez le type de fichiers de façon à ce que les *.xml soient filtrés puis spécifiez le chemin et le nom du fichier XML (on va prendre un exemple fourni avec Delphi : Delphi6\Demos\WebSnap\XSLProducer\recipes.xml ) et cliquez sur Suivant. Nous obtenons l'écran suivant

Image non disponible

Ce document XML décrit une recette de cuisine, avec les ingrédients et les instructions.

Chaque noeud de l'arborescence a été mappé avec un type dédié et hérité des interfaces standard DOM. Vous pouvez cliquer sur chaque noeud et modifier les paramétrages respectifs. Ici nous laisserons la génération de l'assistant intacte. L'étape suivante permet de visualiser l'interface du code généré. Ensuite cliquez sur le bouton Terminer.

Dès lors, une unité est générée. Tous les types nécessaire à la lecture du fichier XML sous jacent sont déclarés et implémentent les interfaces IXMLNode et IXMLNodeCollection selon que les noeuds contiennent des éléments qui se répètent (stagiaires) ou non (stagiaire). 
Trois méthodes utilitaires sont également déclarées et implémentées :

 
Sélectionnez
  1. function GetRecipe(Doc: IXMLDocument): IXMLRecipeType;  
  2. function LoadRecipe(const FileName: WideString): IXMLRecipeType;  
  3. function NewRecipe: IXMLRecipeType; 

GetRecipe  permet de linker le composant XMLDocument à la structure analysée, en gros d'initialiser la racine du document en instanciant une variable de type  IXMLRecipeType .
LoadRecipe  permet de charger un fichier existant.
NewRecipe  offre la possibilité de partir de zéro.

Dans les 3 cas, nous obtenons en retour de fonction une variable de type IXMLRecipeType qui va être notre point de départ pour naviguer dans l'arborescence des données.

Voici un extrait de l'interface générée :

 
Sélectionnez
  1. ...  
  2. { IXMLRecipeType }  
  3. IXMLRecipeType = interface(IXMLNode) ['{F93E8345-8F0D-472A-B9A3-17F365247A42}'] { Accesseurs de propriétés }  
  4. function Get_Name: WideString;  
  5. function Get_Description: WideString;  
  6. function Get_Ingredients: IXMLIngredientsType;  
  7. function Get_Instructions: IXMLInstructionsType;  
  8. procedure Set_Name(Value: WideString);  
  9. procedure Set_Description(Value: WideString);  
  10. { Méthodes & propriétés } 
  11.  property Name: WideString read Get_Name write Set_Name;  
  12. property Description: WideString read Get_Description write Set_Description;  
  13. property Ingredients: IXMLIngredientsType read Get_Ingredients;  
  14. property Instructions: IXMLInstructionsType read Get_Instructions; end; { IXMLIngredientsType } IXMLIngredientsType = interface(IXMLNodeCollection) ['{BAB79B24-ED85-4141-B3C9-A36729D1FEA8}'] { Accesseurs de propriétés }  
  15. function Get_Ingredient(Index: Integer): IXMLIngredientType; { Méthodes & propriétés } function Add: IXMLIngredientType;  
  16. function Insert(const Index: Integer): IXMLIngredientType;  
  17. property Ingredient[Index: Integer]: IXMLIngredientType read Get_Ingredient; default; end; ... 

Enregistrons cette unité et ajoutons là dans les uses de notre application de départ; et déclarons une variable de type IXMLRecipeType :

 
Sélectionnez
  1. var recette : IXMLRecipeType; begin // Initialisons cette variable grâce à la fonction GetRecipe générée par l'expert recette := GetRecipe(XMLDoc); 

Maintenant, la variable  recette  est notre point de départ. Elle a été interfacée par l'assistant de façon à nous faciliter le plus possible son utilisation.
Après avoir chargé le fichier 'recipe.xml' dans le composant XMLDoc via la propriété FileName ... :

Essayez ceci dans un gestionnaire OnClick d'un bouton quelconque (prévoyez également un TMemo pour logger les messages).

 
Sélectionnez
  1. var recette : IXMLRecipeType;  
  2. i: integer; begin recette := GetRecipe(XMLDoc);  
  3. for i := 0 to recette.Ingredients.Count - 1 do memo2.Lines.Add(recette.Ingredients[i].Item);  
  4. end; 


ou alors :

 
Sélectionnez
  1. memo2.Lines.Add(recette.Name); //... memo2.Lines.Add(recette.Ingredients[i].Qty[0].Attributes['unit']); 

Ce dernier exemple montre que dans l'expert, on aurait du décocher la coche [ Répéter ] pour l'attribut  Qty  (cf. schéma ci-dessous) car il s'agit d'un attribut seul et non d'une liste de même attribut dans le document recipe.xml. A vous de le préciser dans l'assistant car ce dernier peut difficilement le deviner.
Il suffit de d'effectuer un double clic sur le composant XMLDocument pour faire apparaître l'expert Liaison de données XML et cette fois-ci, nous sélectionnons le noeud  Qty  et décochons la case Répéter :

Image non disponible

Vous générez à nouveau l'unité, et par rapport à l'unité générée, vous pourrez cette fois coder de la façon suivante :

  • memo2.Lines.Add(recette.Ingredients[i].Qty.Attributes['unit']);  // au lieu de ...
  • memo2.Lines.Add(recette.Ingredients[i].Qty[0].Attributes['unit']);

car  Qty  implémentera  IXMLNode  et non  IXMLNodeCollection .

VII. CONCLUSION


Bref, vous avez là un outil fantastique pour générer vos templates XML et il serait franchement dommage de passer à côté.
Nous n'avons fait que  survoler  ce que Delphi 6 nous apportait en termes de développement XML, il reste encore des découvertes intéressante comme le XML Mapper, outil de transformation des données d'un Dataset vers XML et vice versa et bien d'autres choses encore.
J'y reviendrai bientôt, et en accumulant ce savoir faire, nous serons à même d'utiliser XML à bon escient.
Pensez à votre CV.... :-)))

Sylvain James
Développeur / Formateur

Image non disponible

Livre recommandé  :  XML et les bases de données / Wrox édition Eyrolles