|
begin process at 2008 05 16 21:48:54
Derniers logiciels
|
Trouver une ressource
Vous ne trouvez pas de réponse à votre problème ? Alors posez la question dans le forum.
Souvenez-vous qu'il n'y a jamais de question bête, mais rester dans l'ignorance parce que l'on n'ose pas poser une question, ça c'est une erreur !
XPATH : LE LANGUAGE
Information sur le tutorial
Description
Présentation du language XPath pour faire des requêtes sur un fichier XML
Tutorial
XPath
| Pré-requis: |
- Connaissance d'XML |
|
- Connaissance de base de System.Xml |
IntroductionIl y a quelques années encore, l'XML (eXtensible Markup Language)
avait une importance assez marginale en informatique. Les fichiers de configurations et autres
structures représentant des données étaient décrites avec des moyens rudimentaires comme
les fichiers INI, par exemple.
Aujourd'hui, l'XML occupe une place prépondérante dans le monde de l'informatique et a
remplacé bon nombre d'autres solutions. XPath est un langage qui permet de récupérer une partie
d'un document XML qui nous intéresse, en faisant une requête sur le document.
Ce tutorial sur XPath va donc montrer comment créer une telle requête pour avoir accès aux
informations voulue. Prenez bien note que le but n'est pas de présenter System.Xml.XPath mais bel
et bien la façon dont ont construit une requête (en donnant beaucoup d'exemples).
Pour finir, sachez qu'XPath est extrêmement complet et qu'il ne s'agit pas non plus de présenter
tous les aspects du langage dans cet article, mais seulement les bases. Le reste pouvant être
approfondis par vous-même selon les besoins.
Avant de commencer
Pour tester nos différentes requêtes, on va avoir besoin d'un fichier XML. J'ai choisi de représenter
une petite partie du réseau CodeS-SourceS, en décrivant la hiérarchie des différents éléments, de
manière assez simple. Voici ce qu'on obtient
<CodeS-SourceS>
<csharpfr code = "1080">
<Sources>
<Source name = "XML, XPath" url = "x.aspx">
<Rating> 8 </Rating>
<Author> Bidou </Author>
<Visit>8 343 </Visit>
<Updates>
<Update Date= "15.02.2005"> Correction
d'un bug </Update>
</Updates>
</Source>
<Source name = "Tuto XNA" url = "y.aspx">
<Rating> 8.5 </Rating>
<Author>Mx </Author>
<Visit> 5241 </Visit>
</Source>
<Source name = "Interop"
url = "z.aspx">
<Rating> 10
</Rating>
<Author> Lutinore </Author>
<Visit> 12541 </Visit>
</Source>
</Sources>
<Tutos>
<Source name = "CrossThreading"
url = "a.aspx">
<Rating>9
</Rating>
<Author>Mx
</Author>
<Visit>15241
</Visit>
<Updates>
<Update Date = "22.05.2006">
Mise en forme </Update>
<Update Date = "23.05.2006">
Re Mise en forme </Update>
<Update Date = "24.05.2006">
Re re Mise en forme ! </Update>
</Updates>
</Source>
</Tutos>
</csharpfr>
<vbfrance code = "10345">
<Sources>
<Source name = "RegEx"
url = "s.aspx">
<Rating> 8.4
</Rating>
<Author>Seb
</Author>
<Visit>8821
</Visit>
<Updates>
<Update Date = "02.02.2004">
Ajout d'une option </Update>
<Update Date = "02.03.2004">
Et le Coq alors !? </Update>
<Update Date = "02.03.2004">
Cocorico ! </Update>
</Updates>
</Source>
<Source name = "Thread"
url = "f.aspx">
<Rating>5
</Rating>
<Author>Mx
</Author>
<Visit>1241
</Visit>
<Updates>
<Update Date="22.05.2006">
Euh, oui ? </Update>
</Updates>
</Source>
</Sources>
<Tutos>
<Source name = "VS 2005"
url = "r.aspx">
<Rating> 9.3
</Rating>
<Author> Coq
</Author>
<Visit> 16321
</Visit>
<Updates>
<Update Date = "17.04.2004">
Ajout commentaire </Update>
</Updates>
</Source>
<Source name = "Codyx"
url = "l.aspx">
<Rating> 8.9
</Rating>
<Author>Nix
</Author>
<Visit>10005
</Visit>
</Source>
</Tutos>
</vbfrance>
</CodeS-SourceS>
Maintenant qu'on a la structure du fichier XML, on peut sans autre le représenter sous forme graphique
pour se rendre compte un peu mieux de la forme de notre fichier. Chaque cercle
avec une lettre représente ici un Node.

Voici une table de relation que l'on peut établir si on part du principe que le noeud courant est H. Les valeurs peuvent
être directement utilisée dans la requête XPath.
| Value |
Node |
| self |
H |
| parent |
B |
| child |
I |
| descendant |
I, J, K, L |
| descendant-or-self |
H, I, J, K, L |
| ancestor |
B, A |
| ancestor-or-self |
H, B, A |
| preceding |
E, D, C, F, G |
| preceding-sibling |
C |
| following |
M, N, O, P, Q, R, S, T, U, V, W, X |
| following-sibling |
- |
Les requêtes
Remarques : tous les exemples qui suivent se réfèrent au fichier XML que j'ai décris plus haut.
Pour faire les différentes requêtes, je vais commencer par créer un XmlDocument. C'est une des façons de faire
une requête sur un fichier.
|
// Load the xml file |
|
XmlDocument xDoc = new XmlDocument(); |
|
xDoc.Load("CS.xml"); |
[Rappel: XmlDocument est une sous-classe de XmlNode]
Afin d'alléger le document, je ne propose que la requête pour les exemples. A chaque fois, il faut
donc appeler les méthodes SelectNodes (liste de Nodes) et/ou SelectSingleNode (un Node) qui appartiennent
au XmlDocument.
|
XmlNode xNode = xDoc.SelectSingleNode(xPathExp); |
|
XmlNodeList xNodes = xDoc.SelectNodes(xPathExp); |
Les requêtes de base, sans utiliser de fonctions
Sélection simple
Pour se rendre sur un Node spécifique, il suffit de spécifier son path, voici deux exemples
|
// Retourne l'arborescence à partir de l'élément Sources de csharpfr |
|
"CodeS-SourceS/csharpfr/Sources" |
|
// Retourne une collection de noeuds représentant chaque source de csharpfr |
|
"CodeS-SourceS/csharpfr/Sources/Source" |
Remarque :
L'élément child est omis pour des questions de simplifications. On peut donc faire la modification
suivante :
|
"child::CodeS-SourceS/child::csharpfr/child::Sources" par |
|
"CodeS-SourceS/csharpfr/Sources" |
Sélection avec booléens et égalités
Les crochets permettent de saisir des expressions (conditions , prédicats).
On s'intéresse à toutes les sources qui ont une note plus grande ou égale à 8.5
|
// Les descendants de CodeS-SourceS avec un élément Source qui a un attribut |
|
// plus grand où égal à 8.5 |
|
// Retourne 5 noeuds (les 5 sources ayant la note qui répond au critère) |
|
"CodeS-SourceS/descendant::Source[Rating>=8.5]" |
L'étoile permets de remplacer n'importe quel élément, dans ce cas on obtient pas le même résultat que le précédent
car on cherche uniquement dans les sources, et pas les tutoriaux
|
// Recherche sur tous les sites les Sources (pas les Tutos) qui ont
une source avec |
|
// une note plus grande ou égal à 8.5 |
|
"CodeS-SourceS/*/Sources/Source[Rating>=8.5]" |
On peut combiner des critères avec les opérateurs and et or
|
// Note plus grande que 8 et visite plus grande que 10'000 |
|
"CodeS-SourceS/descendant::Source[Rating>8 and Visit>10000]" |
Cela fonctionne également sur les strings (toutes les sources de Mx)
|
// Les sources de l'auteurs s'appelant Mx |
|
"CodeS-SourceS/descendant::Source[Author='Mx']" |
Ou toutes les sources qui ne sont pas de Bidou
|
// Les sources n'appartenant pas à Bidou |
|
"CodeS-SourceS/descendant::Source[Author!='Bidou']" |
Not peut également être utilisé (toutes les sources qui n'ont pas de note inférieure à six)
|
// Les sources avec une note plus grande ou égal à 6 |
|
"CodeS-SourceS/descendant::Source[not(Rating<6)]" |
Si on s'intéresse aux auteurs distinct, on peut faire quelque chose comme ceci
|
// Tous les auteurs de source (sans doublon) |
|
"//Source[not(Author = preceding::Source/Author)]/Author" |
Vérifie si le Rating courant est plus grand que le rating qui le précède. Dans ce cas, les noeuds suivant nous sont retournés : F, G, I, O, V, X
|
// Rien de très intéressant dans notre exemple: vérifie si le node courant |
|
// est plus grand que celui qui le précède |
|
"//Source[(Rating>preceding::Rating)]" |
Accéder aux Attributs
Les sources avec l'attribut url qui vaut "x.aspx"
|
// Les sources qui ont une url qui vaut x.aspx |
|
"CodeS-SourceS/descendant-or-self::Source[attribute::url='x.aspx']" |
Remarque :
L'élément descendant-or-self est remplacé par // et attribute par @ des questions de simplifications. On peut donc faire la
modification suivante :
|
"CodeS-SourceS/descendant-or-self::Source[attribute::url='x.aspx']" par |
|
"CodeS-SourceS//Source[@url='x.aspx']" |
Expression numérique
Retourne les sites qui ont un nombre de code étant un multiple de 4
|
// Les sites ayant un nombre de
code source étant multiple de quatre |
|
"CodeS-SourceS/*[(@code mod 4) = 0]" |
Les requêtes avec quelques fonctions
En fait, XPath peut aller encore beaucoup plus loin : il contient des fonctions au seins
de son langage. On peut donc faire des sommes et autres manipulations très
pratiques. Dans le cas d'une somme par exemple, il est clair que l'ont doit plus
retourner un set de Nodes comme on le faisait auparavant, mais juste une valeur
numérique.
Il faut donc utiliser un autre objet, le XPathNavigator, qui contient une méthode
d'évaluation. Voici comment on le construit:
|
XPathDocument xPathDoc = new XPathDocument("CS.xml"); |
|
XPathNavigator xPathNav = xPathDoc.CreateNavigator(); |
Comme avant, afin d'alléger le document, je ne propose que la requête pour les
exemples. A chaque fois, il faut donc appeler la méthode Evaluate.
|
object o = xPathNav.Evaluate(xPathExp); |
Sum et Count
Ici, on aimerait trouver le nombre de code disponible sur CodeS-SourceS
|
// Le nombre de code disponible en tout. @ représente toujours un attribut |
|
"sum(CodeS-SourceS/*/@code)" |
On peut aussi très facilement avoir le nombre de site sur CodeS-SourceS
|
// Il s'agit dans notre exemple de vbfrance et csharpfr |
|
"count(CodeS-SourceS/*)" |
Et donc, la moyenne des code par site
|
// On effectue la division (expression numérique) |
|
"sum(CodeS-SourceS/*/@code) div count(CodeS-SourceS/*)" |
Pas mal, non?
Sans XPath en tout cas, il faut pas mal plus de code pour faire la même
fonction. Un grand gain de temps donc, si on sait manipuler la chose.
XPath propose plein d'autres fonctions mais le but n'est pas de toutes les présenter. Sachez simplement qu'on peut effectuer
des opérations sur des strings (substring par exemple), des entiers, etc. Mieux vaut lire la doc et chercher la fonction dont on a besoin sur le
coup, plutôt que de les apprendre toutes par coeur! ;-)
Comparaison
But de l'exercice:
Créer une fonction, une fois avec XPath et une fois sans, qui retourne les
auteurs de codes ayant posté une source avec au moins deux mises à jour dont la
dernière en 2005 ou après ainsi qu'un minimum de 10'000 visites ou une note
plus grande ou égal à 9.
Solution XPath:
|
XPathDocument xPathDoc = new XPathDocument("CS.xml"); |
|
XPathNavigator xPathNav = xPathDoc.CreateNavigator(); |
|
string visitsOrRating = "((Visit>10000) or (Rating>=9))"; |
|
string lastUpdate = "(*/Update[last()][(substring(@Date, 7) > 2004)])"; |
|
string nbUpdate = "(count(*/Update) > 1)"; |
|
string xPathExp = string.Format("//Source[{0}
and {1} and {2}]/Author", visitsOrRating, lastUpdate, nbUpdate); |
| |
// L'appel de compile permet de compiler l'expression
ce qui permet une exécution (beaucoup) plus rapide |
|
XPathNodeIterator authors = xPathNav.Select(xPathNav.Compile(xPathExp)); |
Solution « Standard »:
XmlDocument xDoc = new XmlDocument();
xDoc.Load("CS.xml");
List<string>
result = new List<string>();
foreach (XmlElement
xElementSite in xDoc.DocumentElement)
{
foreach
(XmlNode xNodeSources in xElementSite.ChildNodes)
{
foreach (XmlNode
xNodeSource in xNodeSources.ChildNodes)
{
int
visit = 0;
Int32.TryParse(xNodeSource["Visit"].InnerText,
out visit);
double
rating = 0;
Double.TryParse(xNodeSource["Rating"].InnerText,
out rating);
if
(visit > 10000 || rating >= 9)
{
XmlElement updates =
xNodeSource["Updates"];
if
(updates != null && updates.ChildNodes.Count > 1)
{
string yearStr = updates.LastChild.Attributes["Date"].Value;
int year = 0;
Int32.TryParse(yearStr.Split('.')[2],
out year);
if (year > 2004) result.Add(xNodeSource["Author"].InnerText);
}
}
}
}
}
Résultat et Conclusion
La StopWatch pour mesurer le temps est enclenchée juste avant le Select pour le premier cas, et juste avant le
premier foreach pour le second. Les chiffres dans le tableau plus bas sont une moyenne de plusieurs testes, ils
représentent le nombre de Ticks écoulés.
| File size - nb de match |
Xpath |
Standard |
Coef |
| 3Ko - 1 |
42'408 |
76'488 |
1.8 |
| 157Ko - 1 |
46'864 |
3'667'252 |
78 |
| 242Ko - 90 |
47'796 |
5'257'592 |
110 |
| 425Ko - 90 |
46'964 |
9'356'804 |
203 |
| 6170Ko - 15'228 |
50'988 |
134'623'548 |
2640 ! |
XPath est un langage à part entière tout comme les expressions régulières. La différence étant que la technologie XML est utilisée
presque partout: apprendre comment manipuler les expressions régulières est certainement fort utile, mais dans la pratique on ne
l'utilisera qu'assez rarement.
Apprendre XPath est un très bon investissement qui sera très vite rentabilisé: non seulement le nombre de ligne de codes est moindre, mais en plus les temps de
réponses sont sans appels.
Article XPath sur mon blog
Historique
- 20 octobre 2006 09:29:54 :
- Mise en page
- 20 octobre 2006 09:42:43 :
- Re mise en page
- 20 octobre 2006 09:56:24 :
- Ajout de la couleur dans le code
- 20 octobre 2006 13:06:30 :
- Mise en page de la partie XML
- 20 octobre 2006 13:49:04 :
- Mise en forme du document
- 22 octobre 2006 13:20:25 :
- Mise en page
- 22 octobre 2006 21:30:53 :
- Mise en page(hé oui, encore...)
- 22 octobre 2006 22:05:54 :
- Test
Commentaires
|
CalendriCode
| | | L | M | M | J | V | S | D |
| | | | 1 | 2 | 3 | 4 |
| 5 | 6 | 7 | 8 | 9 | 10 | 11 |
| 12 | 13 | 14 | 15 | 16 | 17 | 18 |
| 19 | 20 | 21 | 22 | 23 | 24 | 25 |
| 26 | 27 | 28 | 29 | 30 | 31 | |
|
|
|