Initiation à XML et Delphi 6
Date de publication : 26/03/2002
Par
Sylvain James (TeamB-FR) (Contributions)
Initiation à XML et Delphi 6
tout doucement, tout simplement...
I. Introduction
II. XML ?
III. XML et Delphi 6
IV. Un début à tout
V. Créer la structure XML
VI. L'expert Liaison de données
VI. Conclusion
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.)
III. XML et Delphi 6
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 :
[SOCIETE]
NOM=4CENT AGENCY
[CONTACT]
NOM=JAMES Sylvain
ADRESSE=LA-BAS
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 :
<Sylvain>
<Societe>4CENT AGENCY</Societe>
<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.
Une requête comme :
SELECT stages.NOMSTAGE, stages.DATESTAGE, stagiaires.NOMSTAGIAIRE, stagiaires.AGESTAGIAIRE
FROM stages,stagiaires
WHERE stages.IDSTAGE = stagiaires.IDSTAGE
va retourner les données sous la forme suivante :
La représentation XML du même jeu de données sera la suivante :
<Stages>
<Stage Nom="WORD 2000 Initiation" Date="14/06/2001">
<Stagiaires>
<Stagiaire Nom="MAGIC Jordan" Age="35"/>
<Stagiaire Nom="ESSONCHA Jean-Roland" Age="38"/>
</Stagiaires>
</Stage>
<Stage Nom="SQL SERVER 7 Administration" Date="11/06/2001">
<Stagiaires>
<Stagiaire Nom="DERIEN Pepito" Age="26"/>
</Stagiaires>
</Stage>
<Stage Nom="DELPHI 5 Perfectionnement" Date="01/06/2001">
<Stagiaires>
<Stagiaire Nom="SUSHI Okimi" Age="42"/>
</Stagiaires>
</Stage>
</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 :
IV. Un début à tout
Jusque 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 :
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 :
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 :
procedure TForm1.btnCreationClick(Sender: TObject);
var
stage,stagiaires,stagiaire : IXMLNode;
begin
memo1.Clear;
XMLDoc.DocumentElement := XMLDoc.CreateElement('stages','');
stage := XMLDoc.DocumentElement.AddChild('stage');
stage.Attributes['nom'] := 'WORD 2000 Initiation';
stage.Attributes['date'] := '14/06/2001';
stagiaires := stage.AddChild('stagiaires');
stagiaire := stagiaires.AddChild('stagiaire');
stagiaire.Attributes['nom'] := 'MAGIC Jordan';
stagiaire.Attributes['age'] := '35';
stagiaire := stagiaires.AddChild('stagiaire');
stagiaire.Attributes['nom'] := 'ESSONCHA Jean-Roland';
stagiaire.Attributes['age'] := '38';
stage := XMLDoc.DocumentElement.AddChild('stage');
stage.Attributes['nom'] := 'SQL SERVER 7 Administration';
stage.Attributes['date'] := '11/06/2001';
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 :
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 :
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.
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 :
function GetRecipe(Doc: IXMLDocument): IXMLRecipeType;
function LoadRecipe(const FileName: WideString): IXMLRecipeType;
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 :
...
IXMLRecipeType = interface(IXMLNode)
['{F93E8345-8F0D-472A-B9A3-17F365247A42}']
function Get_Name: WideString;
function Get_Description: WideString;
function Get_Ingredients: IXMLIngredientsType;
function Get_Instructions: IXMLInstructionsType;
procedure Set_Name(Value: WideString);
procedure Set_Description(Value: WideString);
property Name: WideString read Get_Name write Set_Name;
property Description: WideString read Get_Description write Set_Description;
property Ingredients: IXMLIngredientsType read Get_Ingredients;
property Instructions: IXMLInstructionsType read Get_Instructions;
end;
IXMLIngredientsType = interface(IXMLNodeCollection)
['{BAB79B24-ED85-4141-B3C9-A36729D1FEA8}']
function Get_Ingredient(Index: Integer): IXMLIngredientType;
function Add: IXMLIngredientType;
function Insert(const Index: Integer): IXMLIngredientType;
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 :
var
recette : IXMLRecipeType;
begin
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).
var
recette : IXMLRecipeType;
i: integer;
begin
recette := GetRecipe(XMLDoc);
for i := 0 to recette.Ingredients.Count - 1
do memo2.Lines.Add(recette.Ingredients[i].Item);
end;
ou alors :
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 :
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.
VI. 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
|