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, quelle que soit la plateforme 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 succincts.
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 de 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 plateformes. 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 Delphinaute 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 de XML, au moins le vocabulaire de base (« racine », « élément », « attribut », « nom », etc.).
Rappel : xml.developpez.com
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 :
2.
3.
4.
5.
6.
[SOCIETE]
NOM=4CENT AGENCY
[CONTACT]
NOM=JAMES Sylvain
ADRESSE=LA-BAS
EMAIL=sylvain.james@wanadoo.fr
Un fichier XML comporte des nœuds donc présente une structure arborescente, l'arborescence étant présentée par des indentations de lignes :
2.
3.
<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 :
2.
3.
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 :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
<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 relai dans ces cas-là tout en gardant à l'esprit qu'en termes de performances, XML se révèlera 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 appuierons 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 bibliothèques 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 bibliothèque 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. À savoir que l'on n'est pas obligé d'affecter une liste de chaînes à 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 nœuds 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 un 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 nœud 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 nœud <stages>. Le document doit comporter obligatoirement un root.
Nous allons créer un nœud (IXMLNode) et l'affecter à la propriété DocumentElement. Ce nœud sera le point de départ, la référence à partir de laquelle nous allons créer les autres nœuds, les nœuds enfants.
Rappel technique |
IXMLNode est une classe d'interface et non une classe d'objet. À 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éritées de TInterfacedObject). Une même classe peut implémenter plusieurs interfaces. Les classes XML n'échappent pas à la règle. |
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; //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 : stagiaires 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;
À cette étape, avec deux méthodes et une propriété, nous avons créé un document XML représentant une liste hiérarchisée 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 nœud de type IXMLNode qui est créé.
- La fonction AddChild retourne également le nœud IXMLNode qui est créé, mais il est créé à partir d'un autre nœud et non du document. Le nœud créé devient enfant du nœud 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 nœud 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 nœud de l'arborescence a été mappé avec un type dédié et hérité des interfaces standard DOM. Vous pouvez cliquer sur chaque nœud 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écessaires à la lecture du fichier XML sous-jacent sont déclarés et implémentent les interfaces IXMLNode et IXMLNodeCollection selon que les nœuds 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 :
2.
3.
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 trois 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 :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
...
{ IXMLRecipeType }
IXMLRecipeType = interface
(IXMLNode) ['{F93E8345-8F0D-472A-B9A3-17F365247A42}'
] { Accesseurs de propriétés }
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);
{ Méthodes & propriétés }
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 }
IXMLIngredientsType = interface
(IXMLNodeCollection) ['{BAB79B24-ED85-4141-B3C9-A36729D1FEA8}'
] { Accesseurs de propriétés }
function
Get_Ingredient(Index
: Integer
): IXMLIngredientType; { Méthodes & propriétés }
function
Add: IXMLIngredientType;
function
Insert(const
Index
: Integer
): IXMLIngredientType;
property
Ingredient[Index
: Integer
]: IXMLIngredientType read
Get_Ingredient; default
; end
; ...
Enregistrons cette unité et ajoutons-la dans les uses de notre application de départ et déclarons une variable de type IXMLRecipeType :
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).
2.
3.
4.
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 dû 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. À vous de le préciser dans l'assistant, car ce dernier peut difficilement le deviner.
Il suffit 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 nœud 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 .
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éressantes 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
Livre recommandé : XML et les bases de données / Wrox édition Eyrolles
Liste de mes articles : |
XMLRAD |
DELPHI |
Initiation à XML et Delphi 6 tout doucement, tout simplement… |