Lecture et écriture dans un fichier XML avec XPath

Jean-François Determe

Cet article traite de la façon de lire et de modifier un document XML avec la technologie XPath. ♪

N'hésitez pas à commenter cet article ! Commentez

- Titre : Lecture et écriture dans un fichier XML avec XPath

- Auteur : Jean-François Determe

- Parution : 26 août 2007

- Mise à jour : 26 août 2007

- Licence : Copyright ® 2007 Jean-François Determe. 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. Prérequis

- II. De quoi traite ce tutoriel ?

- III. Présentation du projet

- III-A. Fonctionnalités

- III-B. Techniquement parlant

- IV. Notions XML de base

- IV-A. Nœuds et balises

- IV-B. Racine

- IV-C. Nœud enfant et nœud parent (ou ancêtre)

- IV-D. Attributs

- V. Syntaxe d'XPath

- V-A. Contexte

- V-B. Sélection nœuds/attributs

- V-C. Conditions

- VI. Rechercher des données avec XPath

- VI-A. Structure du fichier XML

- VI-B. Rechercher un nœud à partir de ses attributs

- VI-C. Rechercher un nœud à partir d'un nœud enfant

- VII. Modification de données XML

- VII-A. Édition de données XML

- VII-B. Insertion de nouveaux nœuds/attributs dans un document XML

- VII-C. Suppression d'un nœud et de ses nœuds enfants

- VIII. Améliorations possibles du programme-exemple

- IX. Conclusion

- X. Remerciements

I - Prérequis

- Connaissance du langage C#.
- Les sources du programme-exemple téléchargeables ici : Ici.

II - De quoi traite ce tutoriel ?

Ce tutoriel montre comment rechercher, insérer, modifier et supprimer des données XML à l'aide de la technologie XPath. Pour plus d'informations : Définition XPath. XPath dispose des avantages suivants :

- les performances : XPath permet de traiter rapidement des documents XML ;
- la clarté et la concision du code : XPath permet de coder une même opération en moins de lignes que les autres méthodes.

La première partie porte sur l'extraction de données contenues dans un fichier XML.
La seconde partie traite de l'édition d'un fichier XML.
Ce tutoriel se base sur un projet concret.
Les schémas XSD ne sont pas utilisés ici afin de ne pas rendre trop complexe ce tutoriel.
Cependant, la recherche et l'édition des données dans un fichier XML lié à un schéma XSD ne sont pas des techniques difficiles à apprendre une fois que ce qui est présenté dans ce tutoriel est acquis.

III - Présentation du projet

III-A - Fonctionnalités

Le projet présenté est un gestionnaire de clients élémentaire.
Chaque enregistrement du fichier XML « Client.xml » contient les champs suivants : ID, Prénom, Nom, Téléphone, DateInscription et Status.
Le programme proposé effectue les actions suivantes :
- recherche des données concernant un client à partir de son ID, nom, prénom, téléphone ou date d'enregistrement ;
- insertion, édition et suppression des données concernant un client.

III-B - Techniquement parlant

Ce projet est réparti en trois couches, une qui gère toute l'interface (Interface), une autre qui contient toutes les classes permettant d'accéder aux données (DataLayer) et une dernière qui renferme les classes qui contiennent les données sur un client (StructureLibrary).
L'interface est composée de deux fiches.

IV - Notions XML de base

Cette section décrit les notions XML à connaître pour comprendre la suite de ce tutoriel.

Le fichier XML suivant est utilisé pour expliquer les notions qui suivent :

<?xml version="1.0" encoding="utf-8"?>
<Clients>
<Client id="1">
<Prenom>Jean-Michel</Prenom>
<Nom>Laroche</Nom>
<Telephone>027854632</Telephone>
<DateInscription>02/06/2007</DateInscription>
<Status>Activated</Status>
</Client>
<Client id="2">
<Prenom>Smith</Prenom>
<Nom>Cordwainer</Nom>
<Telephone>024213296</Telephone>
<DateInscription>12/06/2007</DateInscription>
<Status>Activated</Status>
</Client>
<Client id="3">
<Prenom>Frank</Prenom>
<Nom>Herbert</Nom>
<Telephone>022354562</Telephone>
<DateInscription>08/07/2007</DateInscription>
<Status>Activated</Status>
</Client>
<Client id="4">
<Prenom>Phillipe</Prenom>
<Nom>Dick</Nom>
<Telephone>023254789</Telephone>
<DateInscription>12/07/2007</DateInscription>
<Status>Deleted</Status>
</Client>
<MaxID>4</MaxID>
</Clients>

IV-A - Nœuds et balises

Le nœud (node) est l'unité de base d'un fichier XML.
Un nœud est un champ délimité par des balises pouvant contenir une valeur, des attributs et des sous-nœuds (nœuds-enfants).
Dans le fichier XML ci-dessus, <Clients>…</Clients>, <Client id=« 1 »>…</Client>, <Prenom>…</Prenom>… sont des nœuds.
La déclaration d'un nœud ayant comme nom « Telephone » et ayant comme valeur « 99999 » est celle-ci : <Telephone>99999</Telephone>.
Tout élément de la forme <UnNom> est appelé balise (markup).
Un nœud doit être délimité par deux balises de cette façon: <NomDuNœud>Valeur Du Nœud</NomDuNœud>.
Il n'est pas obligatoire de donner une valeur à un nœud (nœud vide).
La valeur (textuelle) d'un nœud est le texte contenu entre les deux balises qui délimitent ce nœud et les éventuelles valeurs textuelles de ses nœuds-enfants.
Déclarer plusieurs nœuds ayant le même nom est permis.

IV-B - Racine

La racine d'un document XML (« root » en anglais) est un nœud fictif qui n'est pas déclaré dans le document.
Ce dernier est le nœud avec le niveau hiérarchique le plus élevé.

L'élément suivant <?xml version=« 1.0 » encoding=« utf-8 »?> n'est pas considéré comme un nœud.

C'est en fait une déclaration qui permet de définir la version d'un fichier XML et son encodage (utf-8, ISO…).

IV-C - Nœud enfant et nœud parent (ou ancêtre)

Le nœud « y » est un nœud enfant (child-node) du nœud « x » si le nœud « y » est contenu dans le nœud « x ».
Dans notre fichier exemple, le nœud <Prenom>Smith</Prenom> est un nœud enfant du nœud <Client id=« 2 »>…</Client>.

Le nœud « y » est le nœud parent (ou l'ancêtre, ancestor/parent-node) du nœud « x » si le nœud « y » contient le nœud « x ».
Dans l'exemple précédent, le nœud <Client id=« 2 »>…</Client> est l'ancêtre du nœud <Prenom>Smith</Prenom>.

IV-D - Attributs

L'attribut (attribute) d'un nœud est un nœud déclaré à l'intérieur de la première balise qui délimite ce nœud.
Dans le fichier-exemple, chaque nœud Client possède un attribut « id ».

V - Syntaxe d'XPath

V-A - Contexte

Le contexte est le référentiel sur lequel la recherche se base.
Pendant la navigation dans l'arborescence d'un fichier XML, l'objet permettant la navigation est toujours positionné sur un nœud ; ce nœud est appelé contexte.
Une expression XPath précédée de « ./ » se base sur le contexte actuel.
Ex. : « ./author » sélectionne tous les nœuds enfants de nom « author » en partant du contexte actuel.

Une expression XPath utilisant comme contexte la racine du document XML est précédée par « / ».
Ex. : « /book » sélectionne tous les nœuds enfants nommés « book » en utilisant comme contexte la racine du document.

Si la recherche porte sur tous les éléments ayant un nom donné sans tenir compte de leur niveau hiérarchique, l'expression XPath est précédée par « // ».
Ex. : « //Friend » sélectionne tous les nœuds du document ayant comme nom « Friend ». Si la recherche doit porter sur tous les nœuds d'un nom donné, mais doit aussi se baser sur le contexte actuel, il convient de faire précéder l'expression XPath par « .// ».
Ex. : « //Prix » sélectionne TOUS les nœuds qui ont ce nom dans le document alors que « .//Prix » ne sélectionne que les nœuds-enfants du contexte actuel ayant pour nom « Prix ».

V-B - Sélection nœuds/attributs

Pour sélectionner tous les nœuds-enfants du contexte actuel, il convient d'utiliser le symbole « * » qui signifie « Tous les nœuds (de type element ou attribut (si précédé de »@« )) ».
Si la sélection porte sur tous les types de nœuds existants, il faut utiliser node() et non * qui se limite aux nœuds de type éléments et attributs.
Ex. : « ./* » sélectionne tous les nœuds-enfants de type élément du contexte actuel.

Pour sélectionner un ou plusieurs attributs, il faut utiliser le symbole « @ ».
Il précède le nom de l'attribut recherché.
Ex. : « ./book/@price » sélectionne tous les attributs price des nœuds book (en se basant sur le contexte actuel).

V-C - Conditions

Pour exprimer une condition sur une recherche, il faut utiliser un (des) bloc(s) délimité(s) par des crochets […].
Exemples
« //Book[price] » sélectionne tous les nœuds du document ayant un nœud enfant nommé price peu importe la valeur de ce dernier.
« //Book[price = '16'] » sélectionne tous les nœuds du document ayant un nœud enfant nommé price dont la valeur est exactement 16.
« //Book[1] » sélectionne uniquement le premier nœud Book parmi les nœuds se nommant Book dans un document XML.
« //Book[price > 15] » sélectionne tous les nœuds book du document dont le nœud enfant price a une valeur plus grande que 15.
« //Book[@author != 'Un mauvais auteur']/price » sélectionne tous les nœuds price pour lesquels la valeur de l'attribut author de leur nœud parent Book est différente de « Un mauvais auteur ».
De plus, les opérateurs logiques « or » et « and » permettent d'émettre plusieurs conditions.

Toutes ces notions théoriques seront appliquées à un cas concret dans les sections qui suivent.

Il est conseillé de lire la MSDN sur la syntaxe XPath (lien : MSDN Library).

Elle est claire et apporte de nombreuses précisions sur la syntaxe XPath ainsi que de nombreux exemples.

VI - Rechercher des données avec XPath

VI-A - Structure du fichier XML

Le fichier XML utilisé est celui-ci:

Clients.xml

<?xml version="1.0" encoding="utf-8"?>
<Clients>
<Client id="1">
<Prenom>Jean-Michel</Prenom>
<Nom>Laroche</Nom>
<Telephone>027854632</Telephone>
<DateInscription>02/06/2007</DateInscription>
<Status>Activated</Status>
</Client>
<Client id="2">
<Prenom>Smith</Prenom>
<Nom>Cordwainer</Nom>
<Telephone>024213296</Telephone>
<DateInscription>12/06/2007</DateInscription>
<Status>Activated</Status>
</Client>
<Client id="3">
<Prenom>Frank</Prenom>
<Nom>Herbert</Nom>
<Telephone>022354562</Telephone>
<DateInscription>08/07/2007</DateInscription>
<Status>Activated</Status>
</Client>
<Client id="4">
<Prenom>Phillipe</Prenom>
<Nom>Dick</Nom>
<Telephone>023254789</Telephone>
<DateInscription>12/07/2007</DateInscription>
<Status>Deleted</Status>
</Client>
<MaxID>4</MaxID>
</Clients>

Chaque nœud Client représente un client et contient cinq nœuds enfants dont les noms indiquent clairement ce que leurs valeurs représentent ; si ce n'est le nœud Status qui prend comme valeur soit « Activated » soit « Deleted ».
Si sa valeur est Deleted, ce nœud sera ignoré dans toutes les recherches.
La démarche qui consiste à ne pas supprimer définitivement un nœud permet la restauration des suppressions involontaires et permet aussi de ne pas réaffecter un ID qui a déjà été utilisé auparavant.
En effet, il n'est pas souhaitable qu'un utilisateur dont le compte a été supprimé veuille se reconnecter avec l'ID qui lui a été fourni lors de son enregistrement et ne réalise pas que le compte auquel il essaye de se connecter n'est en fait plus à lui.

Le nœud MaxID permet de faciliter l'insertion d'un nouveau client dans le fichier XML, car sans ce nœud, la recherche du premier ID non utilisé est plus compliquée.

VI-B - Rechercher un nœud à partir de ses attributs

Recherche d'un client à partir de son ID (à partir d'un attribut)

public static StructureLibrary.Client SearchCustomerById(string ID)
{
/* On déclare et on crée une instance des variables nécessaires pour la recherche */
StructureLibrary.Client Customer = new StructureLibrary.Client();
XPathDocument XPathDocu = new XPathDocument("Clients.xml");
XPathNavigator Navigator;
XPathNodeIterator Nodes;
/* On affecte false à la variable NoMatches afin de vérifier par la suite
* si la recherche a été fructueuse*/
Customer.NoMatches = false;
/* On crée un navigateur */
Navigator = XPathDocu.CreateNavigator();
/* On crée ici l'expression XPath de recherche de client à partir de L'ID */
ExpXPath = "//Client[@id='" + ID + "' and Status != 'Deleted']";
/* On lance la recherche */
Nodes = Navigator.Select(Navigator.Compile(ExpXPath));
/* On vérifie si la recherche a été fructueuse */
if (Nodes.Count != 0)
{
Nodes.MoveNext(); // NOTE: Necéssaire pour se placer sur le noeud recherché
/* Encodage des données dans la classe Customer */
Customer.ID = ID; /* Pas besoin de chercher cette donnée vu que c'est notre
* critère de recherche, on peut donc directement
* l'encoder. */
Nodes.Current.MoveToFirstChild(); /* On se déplace sur le premier noeud
* enfant "Prenom" */
Customer.FirstName = Nodes.Current.Value;
Nodes.Current.MoveToNext(); // On se déplace sur le noeud suivant "Nom"
Customer.Name = Nodes.Current.Value;
Nodes.Current.MoveToNext();
Customer.PhoneNumber = Nodes.Current.Value;
Nodes.Current.MoveToNext();
Customer.RegistrationDate = Nodes.Current.Value;

}
/* Si aucun client n'a été trouvé */
else
{
Customer.NoMatches = true;
}
/* Renvoi de toutes les données dans une instance de la classe "Client" */
return Customer;
}

Cette fonction renvoie une classe Client et permet à partir de son seul argument (qui est l'ID du client) de récupérer toutes les données relatives à ce client.
Premièrement, une instance de la classe Client définie dans Structures.cs est créée, cette classe permet de regrouper en un même objet toutes les données sur un client.
Les propriétés de cette classe sont claires et suffisamment commentées dans le code source, elles ne seront donc pas expliquées ici.
Ensuite, une instance de la classe XPathDocument est créée, cette classe permet le chargement (la mise en mémoire) du document XML dans lequel se trouvent les données sur les clients.
Le constructeur d'un XPathDocument prend un argument : le chemin d'accès au fichier XML qui sera analysé.

La classe XPathNavigator permet de se déplacer dans les nœuds et d'en retirer le contenu.
L'expression XPath fonctionne de la manière suivante :
tout en ignorant les nœuds déclarés comme supprimés (Status != 'Deleted'), elle va rechercher l'ensemble de tous les nœuds « Client » du document ayant comme valeur pour l'attribut ID celle passée en argument à la méthode SearchCustomerByID.

Une fois l'expression XPath créée, la recherche est lancée et tous les nœuds répondant aux conditions de recherche (s'il y en a) sont placés dans un XPathNodeIterator, classe facilitant la navigation dans les nœuds trouvés de cette façon.
La recherche s'effectue avec la méthode Select d'un XPathNavigator.
De plus, l'expression est compilée pour la rendre plus rapide grâce à XPathNavigator.Compile(string XPathExp).

Le nombre de nœuds trouvés est donné grâce à la propriété Count d'un XPathNode Iterator.
Si aucun nœud n'a été trouvé, la valeur True est affectée à Client.NoMatches.

Si au moins un nœud a été trouvé, le XPathNodeIterator est placé dessus grâce à la méthode MoveNext (attention, oublier d'utiliser cette méthode renvoie des résultats erronés lors de l'extraction ou de la modification de données).
Une fois positionné sur le nœud Client, la méthode MoveToFirstChild() est invoquée, celle-ci place le XPathNodeIterator sur le premier nœud enfant (l'ordre correspond aux déclarations des nœuds dans le document).
Ensuite, la valeur du nœud sur lequel le XPathNodeIterator est positionné est encodée (ici « Prenom »).
Pour naviguer dans les nœuds se situant au même niveau (hiérarchique) dans le document, il convient d'utiliser la méthode MoveToNext(), encore une fois, le XPathNodeIterator se place sur le nœud suivant en se basant sur l'ordre des déclarations des nœuds dans le document.

Quand toutes les données sont encodées, la fonction SearchCustomerByID renvoie une classe Client qui contient toutes les données sur le client recherché.

VI-C - Rechercher un nœud à partir d'un nœud enfant

Rechercher un nœud à partir d'un de ses nœuds enfants

public static StructureLibrary.Client SearchCustomerByNode(string Criteria, string Node)
{
/* Pour les commentaires, voir SearchCustomerByID */
StructureLibrary.Client Customer = new StructureLibrary.Client();
XPathDocument XPathDocu = new XPathDocument("Clients.xml");
XPathNavigator Navigator;
XPathNodeIterator Nodes;
Customer.NoMatches = false;
Navigator = XPathDocu.CreateNavigator();
ExpXPath = "Clients/Client["+Criteria+"='" + Node + "' and Status != 'Deleted']";
Nodes = Navigator.Select(Navigator.Compile(ExpXPath));
if (Nodes.Count != 0)
{
Nodes.MoveNext();
Customer.ID = Nodes.Current.GetAttribute("id", "");
Nodes.Current.MoveToFirstChild();
Customer.FirstName = Nodes.Current.Value;
Nodes.Current.MoveToNext();
Customer.Name = Nodes.Current.Value;
Nodes.Current.MoveToNext();
Customer.PhoneNumber = Nodes.Current.Value;
Nodes.Current.MoveToNext();
Customer.RegistrationDate = Nodes.Current.Value;
}
else
{
Customer.NoMatches = true;
}
return Customer;
}

Ce code permet de trouver un nœud à partir de n'importe lequel de ses nœuds enfants.
La méthode SearchCustomerByNode prend deux arguments, respectivement le nom du nœud enfant sur lequel la recherche va se baser pour la recherche et la valeur que ce dernier doit avoir.
NOTE/RAPPEL : la condition de l'expression XPath n'est plus précédée du symbole @, ce qui indique que la condition porte sur un nœud.
Ex. : si le nom du nœud enfant est 'Prenom' et sa valeur attendue 'Toto' alors l'expression prendra cette forme : « Clients/Client[Prenom='Toto' and Status != 'Deleted'] »

L'encodage des données dans une instance de la classe client est similaire à la fonction SearchCustomerByID si ce n'est qu'il faut trouver l'attribut ID avant de l'encoder, car ce n'est plus le critère de recherche.
Pour ce faire, il convient d'invoquer la procédure GetAttribute qui prend deux arguments, le premier étant le nom de l'attribut recherché et le deuxième étant l'espace nom (namespace) du nœud (si aucun espace nom n'est déclaré dans le fichier XML, il convient de laisser le second argument vide).

VII - Modification de données XML

VII-A - Édition de données XML

Modifier des données XML déjà existantes

public static Boolean EditXMLData(StructureLibrary.Client Customer)
{
/* On utilise un XmlDocument et non un XPathDocument, car ce dernier ne permet
* pas l'édition des données XML. */
XmlDocument XmlDoc = new XmlDocument();
XPathNavigator Navigator;
XPathNodeIterator Nodes;
XmlDoc.Load("Clients.xml");
Navigator = XmlDoc.CreateNavigator();
ExpXPath = "//Client[@id='" + Customer.ID + "' and Status != 'Deleted']";
Nodes = Navigator.Select(Navigator.Compile(ExpXPath));
if (Nodes.Count != 0)
{
/* Encodage des nouvelles données */
Nodes.MoveNext();
Nodes.Current.MoveToFirstChild();
Nodes.Current.SetValue(Customer.FirstName);
Nodes.Current.MoveToNext(XPathNodeType.Element);
Nodes.Current.SetValue(Customer.Name);
Nodes.Current.MoveToNext(XPathNodeType.Element);
Nodes.Current.SetValue(Customer.PhoneNumber);
Nodes.Current.MoveToNext(XPathNodeType.Element);
Nodes.Current.SetValue(Customer.RegistrationDate);
XmlDoc.Save("Clients.xml");
return true;
}
else
{
return false;
}
}

Pour éditer des données et non simplement les rechercher, il faut utiliser un XmlDocument à la place d'un XPathDocument.
En effet, l'objet XPathDocument ne permet que de rechercher des données ; c'est pourquoi l'utilisation d'un XmlDocument s'avère nécessaire, car ce dernier permet la modification des données.
Le processus qui instancie un XmlDocument diffère légèrement de celui qui instancie un XPathDocument.
En effet, le constructeur du XmlDocument ne prend aucun argument, le document est chargé à l'aide de la méthode Load du XmlDocument.
Cependant, le processus de recherche est tout à fait identique.

Une fois positionné sur un nœud, le XPathNodeIterator se déplace sur le premier nœud enfant grâce à la méthode MoveToFirstChild().
La modification de la valeur d'un nœud s'exécute à l'aide de la méthode SetValue(string NewValue) dont le seul argument est la nouvelle valeur qui sera affectée au nœud.

Une fois toutes les données éditées, il suffit de sauver le document avec la méthode Save(string FilePath) du XmlDocument qui prend comme argument le chemin où sera créé le nouveau fichier (en l'occurrence, le programme écrase le dernier fichier).

VII-B - Insertion de nouveaux nœuds/attributs dans un document XML

Insertion de nouvelles données dans un document XML

public static Boolean InsertNewCustomer(StructureLibrary.Client Customer)
{
XmlDocument XmlDoc = new XmlDocument();
XPathNavigator Navigator;
XPathNodeIterator Nodes;
Int32 ID; /* Variable utilisée pour savoir quel est l'ID qu'il faut affecter au nouveau
* noeud créé */
XmlDoc.Load("Clients.xml");
Navigator = XmlDoc.CreateNavigator();
/* Recherche du noeud MaxID pour déterminer quel sera l'ID du nouveau
* client. */
ExpXPath = "//MaxID";
Nodes = Navigator.Select(Navigator.Compile(ExpXPath));
Nodes.MoveNext();
/* On place l'ID le plus élevé du document dans la variable ID */
ID = Nodes.Current.ValueAsInt;
/* On incrémente la valeur du noeud MaxID, car une fois notre nouveau noeud
* créé, l'ID le plus élevé du document sera aussi incrémenté */
Nodes.Current.SetValue((ID+1).ToString());
/* On se place sur le noeud ayant l'ID le plus élevé */
ExpXPath = "//Client[@id='"+ID.ToString()+"']";
Nodes = Navigator.Select(Navigator.Compile(ExpXPath));
if (Nodes.Count != 0)
{
Nodes.MoveNext();
/* On crée le noeud principal (Client). */
Nodes.Current.InsertElementAfter("", "Client", "", "");
/* On se place sur le noeud ainsi créé. */
Nodes.Current.MoveToNext(XPathNodeType.Element);
ID++; /* On incrémente ID pour que sa valeur soit identique à celle se
* trouvant dans le noeud MaxID. */
/* Encodage des données */
Nodes.Current.CreateAttribute("", "id", "", ID.ToString());
Nodes.Current.AppendChildElement("", "Prenom", "", Customer.FirstName);
Nodes.Current.AppendChildElement("", "Nom", "", Customer.Name);
Nodes.Current.AppendChildElement("", "Telephone", "", Customer.PhoneNumber);
Nodes.Current.AppendChildElement("", "DateInscription", "", Customer.RegistrationDate);
Nodes.Current.AppendChildElement("", "Status", "", "Activated");
XmlDoc.Save("Clients.xml");
return true;
}
else
{
return false;
}
}

La première opération consiste à récupérer la valeur du nœud MaxID afin de savoir quel ID affecter au futur nœud.
Pour faire cela, le XPathNodeIterator se place sur ce nœud et en extrait la valeur qui est affectée à la variable ID.
La valeur du nœud MaxID est incrémentée de façon à la faire correspondre avec l'ID du nœud qui sera créé par la suite.

Ensuite, l'XPathNodeIterator se place sur le nœud ayant l'attribut ID le plus élevé (celui dont la valeur correspond avec celle de la variable ID).
Une fois le XPathNodeIterator positionné dessus, un nouveau nœud Client est créé ; ce dernier se trouve après tous les autres nœuds.
La création d'un nouveau nœud se fait à l'aide de la méthode InsertElementAfter(string prefix, string LocalName, string NameSpaceURI, string Value).
En l'occurrence, seul l'argument LocalName est intéressant, car c'est cet argument qui détermine le nom du futur nœud.

La méthode InsertElementAfter insère le nouveau nœud après le nœud sur lequel le XPathNodeIterator est actuellement positionné.

Pour insérer le nouveau nœud avant le nœud actuel, il faut utiliser la méthode InsertElementBefore qui prend naturellement les mêmes arguments que son homologue.

La prochaine étape consiste à placer le XPathNodeIterator sur le nouveau nœud créé avec la méthode MoveToNext(XPathNodeType NodeType).
La variable ID est incrémentée de façon à la faire correspondre avec celle du nœud MaxID.
Enfin, l'attribut ID et les nœuds enfants sont encodés avec les méthodes CreateAttribute et AppendChildElement.
La méthode CreateAttribute crée un attribut et prend les mêmes arguments que la méthode InsertElementAfter.
La méthode AppendChildElement crée un nœud enfant et prend également les mêmes arguments que la méthode InsertElementAfter.
Une fois un attribut ou un nœud enfant créé, le XPathNodeIterator ne se place pas automatiquement dessus.

VII-C - Suppression d'un nœud et de ses nœuds enfants

Il n'y a pas de fonction dans le programme-exemple qui permet de supprimer définitivement un nœud.
Pourquoi ? Simplement, car lorsqu’un nœud est définitivement détruit, il n'y a pas moyen de faire une restauration (un rollback).
Le programme ne supprime donc pas le nœud en question, mais affecte la valeur « Deleted » à son nœud Status.
Ainsi, ce client est ignoré dans toutes les recherches et apparait comme supprimé, mais il y a toujours moyen de réactiver son compte.

Cependant, il existe évidemment un moyen de supprimer définitivement un nœud ou un attribut.
La méthode de suppression d'un nœud et de ses nœuds enfants est très simple : il suffit de placer un XPathNodeIterator sur le nœud à supprimer et d'appeler la méthode DeleteSelf qui ne prend aucun argument.
Si l'opération est un succès, le XPathNodeIterator (ou le XpathNavigator) est automatiquement positionné sur l'ancêtre du nœud supprimé.

VIII - Améliorations possibles du programme-exemple

Une série d'améliorations du programme-exemple est proposée en guise d'exercice.

- Lorsque le programme recherche un client par son prénom, il ne prend en compte que le premier résultat, or plusieurs clients peuvent avoir le même prénom.
Il est donc préférable que le programme affiche le nombre total de résultats et permette à l'utilisateur de naviguer parmi ceux-ci (par exemple en renvoyant une array de classes « Client »).
- Créer une fonction permettant de réactiver le compte d'un client en modifiant la valeur de son nœud Status.

IX - Conclusion

La technologie XPath permet d'écrire un code clair et concis tout en en gardant une application performante.
De plus, l'apprentissage de XPath ne demande pas beaucoup d'investissement.
Enfin, cette technologie est intégrée sur d'autres plateformes telles que Java.

X - Remerciements

Merci à Erwy pour ses commentaires sur le fond de l'article.
Merci à Cardi pour ses remarques.
Merci à Skalp pour ses remarques sur l'orthographe.
Merci à Bernard Determe pour ses remarques sur le style de l'article.
Et merci à toute l'équipe de developpez.com qui m'a permis d'héberger cet article.