Utilisation du DOM sous Lazarus/Free Pascal

darrylsite

Le format XML s'impose de plus en plus comme un standard tant pour le stockage de données que pour le partage de données entre applications. Cet article a pour ambition de vous initier à la manipulation du XML sous Lazarus/Free Pascal en utilisant un parseur DOM.

Commentez

- Titre : Utilisation du DOM sous Lazarus/Free Pascal

- Auteur : darrylsite

- Parution : 15 janvier 2010

- 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 ® 2010 Darryl Kpizingui. 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.

Sommaire

- I. Introduction

- II. Notions de base

- II-A. Le format XML

- II-B. Le DOM

- III. Structure du DOM

- III-A. L'interfaçage avec DOM

- III-A-1. La classe TDOMNode

- III-A-2. La classe TDOMNodeList

- III-A-3. La classe TDOMNamedNodeMap

- III-A-4. La classe TDOMDocument

- III-A-5. La classe TDOMElement

- III-A-6. La classe TDOMAttr

- III-B. Les types de nœuds

- IV. Manipulation du DOM

- IV-A. Chargement d'un document XML

- IV-B. Parcours d'un arbre DOM

- IV-C. Création d'un document XML

- IV-D. Modification d'un document XML

- V. Concepts avancés

- V-A. La classe TDOMParser

- V-B. La validation

- VI. Conclusion et remerciements

- VI-A. Conclusion

- VI-B. Liens utiles

- VI-C. Remerciements

I - Introduction

Nous allons utiliser le RAD Lazarus avec le compilateur Free Pascal tout au long de ce tutoriel. Si vous ne le possédez pas encore, vous pouvez le télécharger à cette adresse : https://pascal.developpez.com/telecharger/detail/id/1657/Lazarus.
L'API que nous allons utiliser se trouve déjà dans la distribution standarde de Free Pascal. Nous allons apprendre comment lire, créer et manipuler des données XML en utilisant le DOM. Toutefois, une bonne connaissance du langage Pascal et des concepts de base de la programmation orientée objet seront nécessaires à la compréhension de cet article.

II - Notions de base

II-A - Le format XML

XML (eXtensible Markup Language) ou langage à balises extensibles est un langage utilisé pour stocker et échanger facilement des données entre des applications écrites dans différents langages ou travaillant sous différentes plateformes.
XML est un langage à balises un peu comme le HTML; mais en XML il n'y a pas de balises prédéfinies, l'utilisateur les définit à sa guise en respectant quelques contraintes spécifiées par le modèle.

Exemple d'un fichier XML :

<?xml version="1.0" ?>
<developpez>
<membre>
<pseudo>darrylsite</pseudo>
<message>500</message>
<status>redacteur</status>
</membre>
<membre>
<pseudo>mbr</pseudo>
<message>1500</message>
<status>moderateur</status>
</membre>
</developpez>

Pour manipuler facilement le format XML en programmation, nous utilisons ce qu'on appelle des parseurs XML. Notre choix ici se porte sur un parseur utilisant le modèle DOM qui est disponible dans la distribution standarde de Free Pascal.

II-B - Le DOM

DOM (Document Object Model), est une spécification du w3c pour avoir des API ayant une même interface, et permettant de manipuler, lire et modifier facilement des documents XML.
Le modèle proposé utilise une représentation en mémoire sous forme d'arbre. Le modèle ainsi défini est indépendant de tout langage de programmation, laissant le champ libre aux développeurs du choix de l'implémentation.
En informatique, un arbre est une structure hiérarchique constituée de nœuds qui ont au maximun un parent. Le seul nœud autorisé à ne pas avoir de parent est appelé la racine de l'arbre.

Voici une représentation du document XML précédent sous forme d'arbre :

La racine de l'arbre est le nœud developpez, père des nœuds membre.

III - Structure du DOM

III-A - L'interfaçage avec DOM

Chaque élément constituant l'arbre, est représenté par une classe. La classe principale est TDOMNode dont dérivent plusieurs autres classes. Nous allons passer en revue les principaux attributs et méthodes de ces classes.

III-A-1 - La classe TDOMNode

Chaque élément de l'arbre est un nœud représenté par la classe TDOMNode, ou des classes dérivées.
Cette classe définit différentes propriétés et méthodes permettant d'avoir accès aux informations relatives à un nœud de l'arbre telles que son nom et son type. Les principaux méthodes et attributs de la classe TDOMNode sont :

Attribut ou méthode

Rôle

NodeName : DOMString

Donne le nom du nœud (lecture seule)

NodeType: Integer

Donne le type du nœud (voir plus loin)

Nodevalue : DOMString

Donne la valeur du nœud (lecture+écriture)

function HasChildNodes: Boolean

Indique si le nœud à des nœuds fils

ChildNodes: TDOMNodeList

Donne la liste des nœuds fils (lecture seule)

function HasAttributes: Boolean;

Indique si le nœud contient des attributs

Attributes: TDOMNamedNodeMap

Donne les attributs du nœud sous forme d'une liste (voir plus loin)

III-A-2 - La classe TDOMNodeList

Cette classe définit une liste de nœuds suivant l'ordre du document XML. Cette classe a deux attributs :

Attribut

Rôle

Count : longWord

Donne le nombre de nœuds contenu dans la liste (lecture seule)

Item[longWord]

Permet d'obtenir un élément de la liste. Item est un tableau dont l'indice représente la position de l'élément dans la liste. Le premier élément est à la position 0 et non 1.

III-A-3 - La classe TDOMNamedNodeMap

La classe TDOMNamedNodeMap est similaire à la classe TDOMNodeList puisqu'elle représente aussi une liste de nœuds. Mais les éléments de cette liste sont aussi stockés par clé.

Attribut

Rôle

Length : longWord

Donne le nombre de nœuds contenu dans la liste (lecture seule)

Item[longWord]

Permet d'obtenir un élément de la liste. Item est un tableau dont l'indice représente la position de l'élément dans la liste. Le premier élément est à la position 0 et non 1.

function GetNamedItem(const name: DOMString): TDOMNode;

Permet d'obtenir un nœud de la liste à partir son nom

function SetNamedItem(arg: TDOMNode): TDOMNode;

Insert un nouveau nœud dans la liste

function RemoveNamedItem(const name: DOMString): TDOMNode;

Retire le nœud ayant le nom name de la liste

III-A-4 - La classe TDOMDocument

Cette classe contient l'arbre chargé en mémoire. Mais pour manipuler le DOM, nous utiliserons une classe dérivée de celle-ci : TXMLDocument. Les principaux attributs et méthodes de cette classe sont :

Attribut ou Méthode

Rôle

DocumentElement: TDOMElement

Donne la racine du l'arbre

function CreateElement(const tagName: DOMString): TDOMElement;

Crée un nouveau nœud. La création de tous nouveaux nœuds doit se faire par appel de cette méthode

function GetElementsByTagName(const tagname: DOMString): TDOMNodeList;

Donne la liste des nœuds fils ayant le tag spécifié en paramètre

function CreateTextNode(const data: DOMString): TDOMText;

Crée un nœud textuel contenant la valeur du paramètre data

function CreateAttribute(const name: DOMString): TDOMAttr;

Crée un nouvel attribut qu'on pourra ajouter à un nœud donné

III-A-5 - La classe TDOMElement

Un TDOMElement est une classe dérivée d'un TDOMNode. Elle représente en particulier une balise. Elle permet en grande partie de manipuler les attributs d'un nœud.

Attribut ou Méthode

Rôle

TagName: DOMString

Donne le tag du nœud

function GetAttribute(const name: DOMString): DOMString;

Donne la valeur de l'attribut d'un nœud

procedure SetAttribute(const name, value: DOMString);

Définit un attribut du nœud

procedure RemoveAttribute(const name: DOMString);

Retire un attribut du nœud

function GetElementsByTagName(const name: DOMString): TDOMNodeList;

Liste les nœuds fils ayant le tag spécifié lors de l'appel de la méthode

III-A-6 - La classe TDOMAttr

Cette classe définit l'attribut d'un nœud. Ces principales méthodes permettent de connaitre le nom de l'attribut et d'en modifier la valeur.

Attribut

Rôle

Name: DOMString

Le nom de l'attribut

Value: DOMString

La valeur de l'attribut

III-B - Les types de nœuds

Un arbre DOM est formé d'un ensemble d'entités différentes dérivant de la classe TDOMNode. Pour différencier ces entités, la classe TDOMNode contient une propriété NodeType dont la valeur est de type entier. Afin de faciliter l'utilisation de cette propriété, une liste de constantes a été définie dont voici un tableau récapitulatif :

Constante

Valeur

Rôle

ELEMENT_NODE

1

Une balise de l'arbre

ATTRIBUTE_NODE

2

Attribut d'une balise de l'arbre

TEXT_NODE

3

Nœud textuel d'un arbre

COMMENT_NODE

8

Une balise de commentaire

DOCUMENT_NODE

9

Nœud racine de l'arbre.

DOCUMENT_FRAGMENT_NODE

11

Nœud racine d'un fragment d'arbre

IV - Manipulation du DOM

Avec votre éditeur de texte favori, créer un nouveau fichier texte, copier/coller ou télécharger le document XML ci-dessous.
Voici maintenant venu le moment de parler code : démarrer Lazarus, puis créer un projet personnalisé nommé xmlproject.

<?xml version="1.0" ?>
<developpez>
<membre>
<pseudo>darrylsite</pseudo>
<message>500</message>
<status>redacteur</status>
</membre>
<membre>
<pseudo>mbr</pseudo>
<message>1500</message>
<status>moderateur</status>
</membre>
</developpez>

IV-A - Chargement d'un document XML

Pour travailler avec le DOM, nous aurions à ajouter les unités Dom, Xmlread, Xmlwrite à notre programme. Comme vous pouvez le deviner, l'unité Dom contient la déclaration des classes et fonctions de traitement du XML, Xmlread et Xmlwrite contiennent des routines pour la lecture et l'écriture du d'un document XML dans un fichier ou d'autres sources.

Voici un code qui charge un document XML contenu dans un fichier à l'aide de la méthode ReadXMLFile. test.xml est le nom du fichier sur le disque, et doc la variable qui contiendra l'arbre chargé en mémoire.

program xmlProject;

{$mode objfpc}{$H+}

Uses Dom, XMLRead, XMLWrite;

var doc : TXMLDocument;
racine : TDOMElement;

begin
try
writeln('Chargement ...');
ReadXMLFile(doc, 'test.xml');
Racine:= doc.DocumentElement;
writeln('OK !');
except
writeln('Echoue !');
end;
end.

Si la compilation et l'exécution s'effectuent sans problème, l'arbre chargé en mémoire est contenu dans l'objet doc. L'attribut DocumentElement représente la racine de l'arbre DOM.

On peut toutefois lire un document XML contenu dans une chaîne de la manière suivante

program creation;

{$mode objfpc}{$H+}

Uses Dom, XMLRead, Classes;

var  stream : TStringStream;
doc : TXMLDocument;
chaine : string;

Begin
try
chaine:='<?xml version="1.0" ?><developpez><membre id="n1"><pseudo >darrylsite</pseudo></membre></developpez>';
stream:= TStringStream.Create(chaine);
writeln('Chargement ...');
ReadXMLFile(doc,stream);
writeln('OK !');
except
end.
End.

IV-B - Parcours d'un arbre DOM

Maintenant, voyons un exemple complet qui parse un document XML, parcourt tous les nœuds et donne leurs valeurs.

Nous commencerons par charger l'arbre en mémoire depuis le fichier test.xml

ReadXMLFile(Doc, 'test.xml');

Puis, nous obtenons l'élément racine et listons tous ses nœuds fils contenant le tag membre

racine:=doc.DocumentElement;
liste:=racine.GetElementsByTagName('membre');

liste.item un tableau de nœuds fils du nœud racine. Le premier élément est à la position 0 et contient liste.count éléments.
Enfin, l'affichage de la valeur du nœud et du texte qu'il contient se fait par :

writeln( lst.item[j].nodeName, ' : ', lst.item[j].TextContent);

Le code complet permettant le parcours de l'arbre DOM est le suivant

program Parcours;
{$mode objfpc}{$H+}
Uses Dom, XMLRead, xmlWrite;

var doc    : TXMLDocument;
racine : TDOMElement;
liste, lst  : TDOMNodeList;
taille, etaille, i, j : LongWord;

begin
try
{partie 1 : chargement et creation de l'arbre}
writeln('Chargement ...');
ReadXMLFile(Doc, 'test.xml');
writeln('OK !');

{partie 2 : }
racine:=doc.DocumentElement;
liste:=racine.GetElementsByTagName('membre');
taille:= liste.Count;

{partie 3}
for i:=0 to taille-1 do
begin
lst:=liste.Item[i].GetChildNodes;
etaille := lst.count;
writeln('------------------------------------------------');
for j:=0 to etaille-1 do
begin
{partie 4}
writeln( lst.item[j].nodeName, ' : ', lst.item[j].TextContent);
end;
end;
except
writeln('Echoue !');
end;
readln;
end.

Pour parcourir l'arbre DOM, on dispose aussi des méthodes suivantes faisant partie de la classe TDOMNode :

Attribut

Rôle

FirstChild: TDOMNode

Représente le premier nœud fils du nœud courant

LastChild: TDOMNode

Représente le dernier nœud fils du nœud courant

NextSibling: TDOMNode

Pointe sur le nœud frère suivant. C'est en fait le nœud qui suit le nœud courant dans la liste de ParentNode.ChildNodes. Il contient nil si c'est le dernier fils

PreviousSibling: TDOMNode

Pointe sur le nœud frère précédent. C'est en fait le nœud qui précède le nœud courant dans la liste de ParentNode.ChildNodes. Il contient nil si c'est le premier fils

ParentNode: TDOMNode

Représente le nœud parent

IV-C - Création d'un document XML

Maintenant que nous savons lire un document XML, nous allons voir comment on peut en créer par programmation.
Le procédé est simple. Nous allons créer le document (TXMLDocument), puis créer l'élément racine,

doc:= TXMLDocument.create;
racine:=doc.CreateElement('pascal');

Un fois le nœud racine crée, nous ajoutons ce nœud comme la racine de l'arbre DOM. Les méthodes createXXX permettent de créer différents types de nœuds : createElement , createTextNode , createAttribute .

doc.AppendChild(racine);

Pour ajouter un nouveau nœud fils, nous utilisons la méthode AppendChild d'un TDOMNode dont voici le prototype :

function AppendChild(NewChild: TDOMNode): TDOMNode;

Enregistrer le document XML dans un fichier se fait simplement par le code suivant où create.xml est le nom du fichier sur le disque.

writexmlfile(doc, 'create.xml');

Le code complet permettant la création d'un document XML est le suivant

program xmlproject2;
{$mode objfpc}{$H+}

uses dom, xmlwrite;

var doc               : TXMLDocument;
racine, ele, fils : TDOMElement;

begin

try
{partie 1}
doc:= TXMLDocument.create;
racine:=doc.CreateElement('pascal');
doc.AppendChild(racine);
{partie 2}
ele:= doc.CreateElement('compilateur');
racine.AppendChild(ele);
ele.AppendChild(doc.CreateTextNode('free pascal'));
ele.SetAttribute('version', '2.2.0');
{partie 3}
ele:= doc.CreateElement('compilateur');
racine.AppendChild(ele);
ele.SetAttribute('version', '7.0');
fils:=doc.CreateElement('nom');
fils.AppendChild(doc.CreateTextNode('Turbo Pascal'));
ele.AppendChild(fils);
fils:=doc.CreateElement('type');
fils.AppendChild(doc.CreateTextNode('console'));
ele.AppendChild(fils);
{partie 4}
writexmlfile(doc, 'create.xml');

writeln('OK !');
except
writeln('echoue');
end;

readln;
end.

Il est important de créer un nouveau nœud avec doc.createElement() car un nœud donné contient toujours une référence vers l'arbre DOM auquel il appartient.
Dans la première partie, on crée la racine en ajoutant le nœud crée au document XML. La deuxieme partie se diffère un peu de la première en ce sens que le nœud créé est ajouté comme nœud fils de la racine de l'arbre DOM. Le nouveau nœud contient à son tour un nœud fils ne contenant que du texte. Le reste du code peut se passer de tout commentaire. Nous reviendrons sous peu sur l'utilisation des attributs.

En exécutant ce programme, le document XML créé est enregistré sur le disque sous le nom « create.xml ».

<?xml version="1.0"?>
<pascal>
<compilateur version="2.2.0">free pascal</compilateur>
<compilateur version="7.0">
<nom>Turbo Pascal</nom>
<type>console</type>
</compilateur>
</pascal>

Pour enregistrer le document XML dans une chaîne, on peut procéder comme suit

//var st : TStringStream; //contenue dans l'unité classes
st:=TStringStream.Create('');
writexml(racine, st); writeln(st.DataString);

IV-D - Modification d'un document XML

Nous nous proposons de modifier le document XML que nous avons créé dans l'exemple précédant. Nous allons modifier l'attribut version de Free Pascal en 2.2.1, supprimer le compilateur Turbo Pascal, puis ajouter un nouveau compilateur MIDletPascal.

Voici d'autres méthodes de la classe TDOMNode que nous allons utiliser :

Méthode

Rôle

function RemoveChild(OldChild: TDOMNode): TDOMNode;

Retire un nœud fils du nœud courant

function ReplaceChild(NewChild, OldChild: TDOMNode): TDOMNode;

Remplace un nœud fils du nœud courant

function HasAttributes: Boolean;

Indique si le nœud contient au moins un attribut

Et quelques méthodes de TDOMElement:

Méthode

Rôle

function GetAttribute(const name: DOMString): DOMString;

Donne la valeur de l'attribut dont le nom est passé en paramètre

procedure SetAttribute(const name, value: DOMString);

Modifie la valeur de l'attribut dont le nom est passé en paramètre. Si l'attribut n'existe pas encore, il sera créé

procedure RemoveAttribute(const name: DOMString);

Retire l'attribut dont le nom est passé en paramètre de l'élément.

Dans le code suivant, seule la procédure Modification permet la modification du l'arbre DOM. Le reste du reste est donné par soucis de complétude et pour servir d'exemple pour un parcours approfondi de l'arbre.

program pmodif;
{$mode objfpc}{$H+}
uses dom, xmlRead, xmlWrite;

procedure espace(sp : word);
var i : word;
begin
if sp<>0 then
sp:=sp+1;
for i:=1 to sp do
write(' ');
end;

procedure affichage(element : TDOMElement; prof : word);
var liste : TDOMNodeList;
i, t, j : word;
begin
liste:= element.ChildNodes;
t:= liste.Count-1;
for i:=0 to t do
begin
if liste.Item[i].HasChildNodes then
begin
espace(prof);
write('<', liste.Item[i].NodeName, ' ');
if (liste.Item[i].HasAttributes) then
begin
for j:= 0 to liste.Item[i].Attributes.Length-1 do
write(liste.Item[i].Attributes.Item[j].NodeName, '="',liste.Item[i].Attributes.Item[j].NodeValue, '" ');
end;
writeln('>');
affichage(TDOMElement(liste.Item[i]), prof+1);
espace(prof);
writeln('</', liste.Item[i].NodeName, '>');
end
else
begin
espace(prof);
writeln(liste.Item[i].NodeValue);
end;
end;
end;

procedure modification(rac : TDOMElement);
var liste : TDOMNodeList;
element : TDOMElement;
begin
liste:= rac.GetElementsByTagName('compilateur');
{modification de free pascal}
TDOMElement(liste.Item[0]).SetAttribute('version', '2.2.1');
TDOMElement(liste.Item[0]).SetAttribute('ide', 'lazarus');
{suppression de Turbo Pascal}
rac.RemoveChild(liste.Item[1]);
{ajout de midlet pascal}
//toujours crÚe l'element a partir du TXMLDOcument
element:=rac.OwnerDocument.CreateElement('compilateur');
element.SetAttribute('version', '2.2');
element.AppendChild(rac.OwnerDocument.CreateTextNode('Midlet Pascal'));
rac.AppendChild(element);
end;

var doc : TXMLDocument;
racine : TDOMElement;

begin
try
readXmlFile(doc, 'create.xml');
racine:= doc.DocumentElement;
writeln('Avant : ');
affichage(racine,  0);
writeln('**************************');
writeln('Apres :');
modification(racine);
affichage(racine,  0);
writeXmlFile(doc, 'modifcreate.xml');
except
writeln('Echoue !');
end;
readln;
end.

L'exécution du programme donne le fichier XML suivant :

<?xml version="1.0"?>
<pascal>
<compilateur ide="lazarus" version="2.2.1">free pascal</compilateur>
<compilateur version="2.2">Midlet Pascal</compilateur>
</pascal>

V - Concepts avancés

V-A - La classe TDOMParser

Pour utiliser des concepts plus avancés tels que la validation d'un document XML ou l'utilisation des espaces de noms, nous aurons recours à un objet TDOMParser. Cette classe permet aussi de mettre en œuvre une gestion avancée des erreurs pouvant survenir lors du traitement d'un document XML.

TDOMParser = class(TObject)
private
{...}
public
constructor Create;
destructor Destroy; override;
procedure Parse(Src: TXMLInputSource; out ADoc: TXMLDocument);
procedure ParseUri(const URI: WideString; out ADoc: TXMLDocument);
function ParseWithContext(Src: TXMLInputSource; Context: TDOMNode; Action: TXMLContextAction): TDOMNode;
property Options: TDOMParseOptions read FOptions;
property OnError: TXMLErrorEvent read FOnError write FOnError;
end;

Un objet TDOMParser pour être utilisé, doit être l'attribut d'une classe donnée afin de mettre en œuvre la gestion des erreurs.
La propriété onError definit la fonction à appeler quand une erreur survient lors du traitement du document XML. Options définit les paramètres qui vont permettre la validation d'un document XML ainsi que l'utilisation des espaces de noms.

Nous allons construire une classe qui nous permettra de parser et de valider un document XML contenu dans un fichier à l'aide de la classe TDOMParser.

type myParseur=class
private

error : boolean;
errMessage : String;
path : string;
validate : boolean;
ns : boolean;

procedure onError(Err: EXMLReadError);

public

constructor init(pth : String);
procedure setValidate(b : boolean);
procedure setNameSpace(b: boolean);
function doParse(): TXMLDocument;
function isError() : boolean;
function getErrorMessage() : String;

end;

constructor myParseur.init(pth : String);
begin
self.path:=pth;
error:=false;
errMessage:='';
validate:=false;
ns:=false;
end;

procedure myParseur.setValidate(b : boolean);
begin
validate:=b;
end;

procedure myParseur.setNameSpace(b: boolean);
begin
ns:=b;
end;

function  myParseur.doparse() : TXMLDocument;
var fichier : textFile;
source : TXMLInputSource;
sourceFic : TFileStream;
doc : TXMLDocument;
parseur : TDOMParser;
begin

try
sourceFic:= TFileStream.Create(path, fmOpenRead);
source:= TXMLInputSource.create(sourceFic);

except
doParse:=nil;
error:=true;
errMessage:='Impossible de lire le fichier '+path;
exit;
end ;

doc:=nil;
parseur:= TDOMParser.Create;
parseur.OnError:=@onError;
parseur.Options.Validate:=validate;
parseur.Options.Namespaces:=ns;
try
parseur.Parse(source, doc);
except
end;

doParse:=doc;
end;

function myParseur.isError() : boolean;
begin
isError:=error;
end;

function myParseur.getErrorMessage() : String;
begin
getErrorMessage:=errMessage;
end;

procedure myParseur.onError(Err: EXMLReadError);
begin
error:=true;
errMessage:=err.ErrorMessage;
end;

La méthode onError() est la méthode qui sera appelée dynamiquement par le programme quand une erreur survient lors du traitement du document. Une référence vers cette fonction doit être passée à l'attribut onError de la classe TDOMParser.

parseur.OnError:=@onError;

Le reste du code peut se passer de tout commentaire puisque je suppose que vous avez déjà acquis les bases de la programmation objet.

V-B - La validation

Vous savez sans doute qu'en XML il n'y a pas de balises prédéfinies. On peut donc définir les balises comme on le souhaite et les placer n'importe où dans un document. Mais pour être sûr qu'un document XML soit compris par une autre application, nous devons définir un protocole de communication qui servira à valider le document XML.

Pour valider un document XML, on peut soit utiliser un DTD (Document Type Definition) ou un document XSD (XML Schema Definition). A ma connaissance, l'API que nous utilisons supporte uniquement l'utilisation de DTD pour la validation d'un document XML.

Pour valider un document XML, nous allons utiliser la classe TDOMParser, mais nous allons modifier le paramètre Options

TDOMParseOptions = class(TObject)
private
{...}
public
property Validate: Boolean read FValidate write FValidate;
property PreserveWhitespace: Boolean read FPreserveWhitespace write FPreserveWhitespace;
property ExpandEntities: Boolean read FExpandEntities write FExpandEntities;
property IgnoreComments: Boolean read FIgnoreComments write FIgnoreComments;
property CDSectionsAsText: Boolean read FCDSectionsAsText write FCDSectionsAsText;
property ResolveExternals: Boolean read FResolveExternals write FResolveExternals;
property Namespaces: Boolean read FNamespaces write FNamespaces;
end;

Le paramètre qui nous interesse ici est Options.Validate. Sa valeur à true indique qu'on desire valider le document à parser à l'aide d'un DTD. Nous allons parser et valider le document XML suivant :

<?xml version="1.0" ?>
<!DOCTYPE developpez
[
<!ELEMENT developpez (membre*)>
<!ELEMENT membre (pseudo, message, status)>
<!ATTLIST membre id ID #REQUIRED>
<!ELEMENT pseudo (#PCDATA)>
<!ELEMENT message (#PCDATA)>
<!ELEMENT status (#PCDATA)>
]
>
<developpez>
<membre id="n1">
<pseudo >darrylsite</pseudo>
<message>500</message>
<status>redacteur</status>
</membre>
<membre id="m2">
<pseudo>mbr</pseudo>
<message>1500</message>
<status>moderateur</status>
</membre>
</developpez>

Voici un exemple qui fait le travail voulu :

program validation;

{$mode objfpc}{$H+}

uses dom, xmlRead, xmlWrite, classes;

type myParseur=class
{...}
end;

var
parseur : myParseur;
doc     : TXMLDocument;

begin

parseur:= MyParseur.init('test.xml');
parseur.setValidate(true);//on indique ici qu'on veut valider le xml
doc:=parseur.doParse();
if(parseur.isError()) then
begin
writeln('Erreur : '+parseur.getErrorMessage())
end
else
writeln('Le document xml est conforme');

readln;
end.

En exécutant ce code, vous aurez un message disant que le document est conforme. Mais si on change, par exemple, la première balise membre par membres, une erreur survient avec le message Using undeclared attribute 'id' on element 'membres'.

VI - Conclusion et remerciements

VI-A - Conclusion

A travers ce tutoriel, et avec des exemples précis et complets, vous avez pu apprendre les bases de l'API DOM permettant de manipuler des données XML avec Lazarus/Free Pascal. Ce tutoriel ne couvre pas toute la spécification XML : les espaces de noms, et bien d'autres caractéristiques n'ont pas été abordés, laissant la possibilité aux lecteurs de continuer leurs recherches. Puisqu'il n'y a pas de documentation officielle sur l'API, le lecteur pourra toutefois consulter le code source de l'API pour plus de détails.

VI-B - Liens utiles

Concepts de base du XML
Introduction à la Programmation Orientée Objet

VI-C - Remerciements

Je tiens à remercier Bérénice MAUREL, Alcatîz ainsi que krachik pour leurs relectures et remarques.