INaLCO - M2 Ingénierie Multilingue
Techniques XML, cours n°3 - version
provisoire
Jean-François Perrot
DOM (Document Object Model)
- I. Notion de parseur
XML, SAX & DOM
- Principe général
- L'exemple
classique de la compilation des programmes
- Ce que XML
apporte
- Modalités
- Deux
manières de lire un fichier XML : SAX & DOM
- Généralités
sur DOM :
- II. DOM - Java
- L'interface Node
- L'interface
Document
- L'interface
Element
- III. Un exemple
simple
I. Notion de parseur XML, SAX & DOM
-
Vous
avez dit, parseur ?
On fit aussi analyseur syntaxique...
Cet objet omniprésent en informatique résout le problème très général
que voici :
- les arbres vivent en mémoire centrale (représentés par
des jeux de pointeurs)
- pour engendrer et échanger des données on a besoin de
fichiers (sur disque ou à travers le réseau)
- il faut donc décoder ces fichiers pour construire les
arbres en mémoire centrale
Vu sous un autre angle :
- les textes qui sont soumis à un ordinateur codent en
général des arbres
(c'est le cas pour les textes de programmes, voir ci-dessous,
c'est aussi le cas pour les textes HTML ou XML, le but du balisage
étant justement de donner une structure d'arbre au texte sous-jacent)
- leur traitement demande en premier lieu que leur nature
arborescente soit révélée
- c'est pourquoi la première phase du traitement d'un
fichier-texte est toujours une analyse syntaxique.
L'analyse syntaxique est donc le processus qui fait passer d'un texte
(structure unidimensionnelle) à un arbre (structure bidimensionnelle).
La suite du traitement consiste en des transformations d'arbres, puis
en une interprétation du dernier arbre produit.
Par exemple, la visualisation d'une fichier XML dans une page Web par
le truchement d'une feuille de style XSLT
passe par trois étapes :
- Lecture et analyse syntaxique du fichier XML,
construisant l'arbre associé ;
- Transformation de cet arbre en un arbre HTML par
l'action de la feuille de style ;
- Interprétation de l'arbre HTML par le navigateur,
aboutissant à sa visualisation dans la fenêtre.
-
L'exemple
classique de la compilation des programmes
Dans un compilateur (traducteur d'un texte-source en langage-machine)
on distingue deux étapes :
- l'analyse syntaxique qui transforme le texte-source
(écrit en syntaxe concrète)
en un arbre (l'arbre syntaxique) représenté en machine
- le générateur de code (assembleur) qui travaille sur
l'arbre syntaxique.
Le parseur doit tenir compte
- de la syntaxe concrète (grammaire du langage)
- et de la structure de l'arbre syntaxique (et de sa
représentation en machine)
Exemple : "le même programme" en syntaxe C :
int a, b, q, r ;
main () {
scanf("%d", &a);
scanf("%d", &b); q = 0; r = a;
while (r >= b) {
q = q+1; r = r-b;
}
printf("%d", q); printf("%d",
r);
}
et en syntaxe Ada :
with Ada.Integer_Text_Io; use Ada.Integer_Text_Io;
procedure Main is
a, b, q, r : Integer;
begin get(a); get(b); q := 0; r := a;
while r >= b
loop q := q+1; r := r-b; end loop;
put(q); put(r);
end Main;
Si on change de syntaxe concrète il faut changer de parseur !
Il faut aussi changer de parseur si on change la syntaxe abstraite !
Soulignons que la représentation d'un arbre en machine pose deux
niveaux de problèmes :
- celui de la "forme" de l'arbre, qui est
approximativement pris en compte par un dessin comme celui qui précède.
- celui de son implantation en mémoire (organisation des
pointeurs),
qu'on ne peut aborder pratiquement qu'à travers un langage de
programmation.
-
Ce que
XML apporte
XML offre un moyen pour écrire "directement" l'arbre syntaxique,
plus exactement, il offre une syntaxe concrète uniforme (mais peu
lisible) applicable à toute sorte d'arbres.
Exemple : le programme ci-dessus en XML
fichier qr.xml
<?xml version='1.0'?>
<Prog>
<Variables> <Var nom="a"/> <Var nom="b"/> <Var nom="q"/> <Var nom="r"/>
</Variables>
<Sequence>
<Lecture> <Var nom="a"/> </Lecture>
<Lecture> <Var nom="b"/> </Lecture>
<Affectation> <Var nom="q"/> <Cte val="0"/> </Affectation>
<Affectation> <Var nom="r"/> <VarExp> <Var nom="a"/> </VarExp> </Affectation>
<Boucle>
<Comparaison op=">="> <VarExp> <Var nom="r"/> </VarExp>
<VarExp> <Var nom="b"/> </VarExp>
</Comparaison>
<Sequence>
<Affectation> <Var nom="q"/>
<Bin op="+"> <VarExp> <Var nom="q"/> </VarExp> <Cte val="1"/> </Bin>
</Affectation>
<Affectation> <Var nom="r"/>
<Bin op="-"> <VarExp><Var nom="r"/></VarExp>
<VarExp><Var nom="b"/></VarExp>
</Bin>
</Affectation>
</Sequence>
</Boucle>
<Ecriture> <VarExp> <Var nom="q"/> </VarExp> </Ecriture>
<Ecriture> <VarExp> <Var nom="r"/> </VarExp> </Ecriture>
</Sequence>
</Prog>
avec le Document Object Model (DOM) il offre aussi
une syntaxe abstraite,
c'est à dire une définition précise des arbres
correspondant aux fichiers XML.
-
Modalités
-
Deux
manières de lire un fichier XML : SAX & DOM
- La voie SAX : Simple API for XML
- L'idée : un parseur à événements :
on décharge le programmeur de la tâche de reconnaître les mots du texte
- chaque fois que le parseur a trouvé quelque
chose d'intéressant
(une balise ouvrante, une balise fermante)
il le signale par un événement
- le programmeur doit traiter ces
événements pour obtenir le résultat qu'il s'est fixé.
- Le traitement en question dépend en général
(comme le résultat cherché)
de la forme du fichier...
il est réalisé par une séquence d'actions
cette séquence d'actions peut construire un
objet-résultat...
- Il s'agit donc d'une programmation procédurale,
de "bas niveau",
délicate à mettre en œuvre et difficile à systématiser.
Nous n'en parlerons pas dans ce cours.
- La voie DOM : Document Object Model
- L'idée est de définir une structure d'objet
assez générale pour représenter tous les arbres XML
cette structure est connue sous le nom d'API DOM
- Muni de cette structure, on peut écrire un
parseur universel
qui analyse n'importe quel fichier XML
et produit l'arbre DOM correspondant (en mémoire centrale).
Reste à exploiter cet arbre...
- Le travail sur l'arbre se ramène le plus
souvent à un parcours récursif,
qui ne présente que des difficultés de détail, comme on le verra ici.
- Pourquoi ces deux voies concurrentes ?
D'abord, parce que chronologiquement SAX a précédé DOM.
Ensuite parce que DOM peut entraîner une occupation mémoire excessive
dans le cas de gros fichiers,
puisque l'arbre est construit tout entier à la lecture du fichier,
alors que SAX judicieusement employé permet de n'avoir à chaque instant
en mémoire qu'une "fenêtre"
de taille bornée. Nous n'aborderons pas ce problème dans ce cours.
-
Généralités
sur DOM :
- Comme l'indique son nom de Document Object
Model, il s'agit d'un modèle exprimé dans le cadre
conceptuel
des catégories de base des langages à objets : instance,
classe, héritage.
Ces structures de base se réalisent de manières diverses dans les
différents langages de programmation,
par exemple, Java ajoute à la notion de classe celle d'interface.
Mais elles sont suffisamment solides pour le modèle reste
substantiellement le même dans ses diverses incarnations langagières
(voir plus loin).
Il faut aussi penser que le cadre conceptuel en question est bien
pauvre et qu'il représente une contrainte :
il a sans doute fallu gauchir de temps en temps le modèle pour le faire
entrer dans le cadre !
- Le modèle autorise des variantes : Cœur (Core),
DOM-XML et DOM-HTML,
qui sont le produit de son histoire.
- Son élaboration s'est faite par étapes
chronologiques : DOM level 1, 2, 3
Sur ces deux points, voir http://en.wikipedia.org/wiki/Document_Object_Model
- Il faut bien distinguer trois niveaux de réalité :
- La spécification abstraite
(par le W3C), pur modèle
indépendant du langage de
programmation.
Il s'exprime en IDL (Interface description language),
plus précisément celui de Corba.
Voir DOM
Level2 Core et pour la traduction française
http://www.yoyodesign.org/doc/w3c/dom2-core/core.html
- La spécification concrète
(par le W3C),
dans un langage de programmation choisi (language binding)
comme Java, PHP, JavaScript, ou C++.
En Java, cette spécification ne contient que des interfaces,
elle se matérialise par le package org.w3c.dom.
Voir ce que dit le W3C côté spécification) :
http://www.w3.org/TR/DOM-Level-2-Core/java-binding.html
et aussi les explications de la
doc Java standard (côté implémentation) :
http://java.sun.com/j2se/1.5.0/docs/api/org/w3c/dom/package-summary.html
- L'implémentation effective
(en Java, par un système de classes implémentant
les interfaces susdites).
Cette implémentation est soumise à concurrence entre vendors
(libéralisme oblige).
Nous n'utiliserons que :
Ces deux implémentations font à peu près la même
chose,
mais pas exactement de la même façon !
Dans ce cours, nous nous concentrerons sur la
réalisation de DOM en Java.
Pour C++, voir l'implémentation Apache-Xerces.
Sur la réalisation homologue en PHP-5, on lira (outre le
Manuel PHP) un excellent
tutoriel introductif chez Développez.com.
La version JavaScript, qui se spécialise dans le traitement de HTML
est quelque peu différente dans le détail, nous n'en parlerons pas ici.
II.
Éléments principaux du DOM (pour le détail de la réalisation en Java,
voir le bréviaire)
-
L'interface
Node
L'interface Node est le type de données principal
pour le modèle objet
de document dans son ensemble.
Elle représente un seul nœud dans
l'arbre du document.
Bien que tous les objets mettant en œuvre
l'interface Node exposent des méthodes
pour la gestion des enfants, certains ne peuvent pas avoir d'enfants.
Par exemple, les nœuds Text ne
peuvent pas avoir d'enfants,
et ajouter un enfant à ces nœuds soulève
alors une exception DOMException.
Les attributs nodeName, nodeValue
et attributes composent un mécanisme
permettant d'accéder aux informations d'un nœud sans devoir faire appel
aux interfaces dérivées spécifiques.
Au cas où il n'y aurait pas de
correspondance évidente entre ces attributs et un type de nœud
particulier
(par exemple, nodeValue pour un objet Element
ou attributes
pour un objet Comment),
la valeur null est retournée.
Remarquer que les
interfaces spécialisées peuvent offrir des mécanismes supplémentaires
plus pratiques
pour obtenir ou fixer les informations pertinentes.
-
L'interface
Document
L'interface Document représente le document HTML,
ou XML, entier.
Conceptuellement, c'est la racine de l'arbre du document, qui offre
l'accès primaire aux données du document.
Puisque les éléments, les nœuds de texte, les commentaires, les
instructions de traitement, etc.,
ne peuvent pas exister en-dehors du
contexte d'un Document,
l'interface Document contient aussi les
méthodes nécessaires pour créer ces objets.
Les objets Node créés
possèdent un attribut ownerDocument qui les
associent au Document
dans
le contexte duquel ils ont été créés.
-
L'interface
Element
L'interface Element représente un élément dans un
document HTML ou XML.
Les éléments peuvent avoir des attributs associés ;
comme l'interface Element hérite de celle de
Node, on peut utiliser l'attribut générique attributes
de l'interface Node pour récupérer le jeu de tous
les attributs d'un élément.
L'interface Element possède des méthodes pour
récupérer par leur nom soit un objet Attr,
soit une valeur d'attribut.
- En XML, où une valeur d'attribut peut contenir des
appels d'entité,
on devrait récupérer un objet Attr pour examiner
le sous-arbre, qui peut être très complexe,
représentant la valeur d'attribut.
- En ce qui concerne HTML, où tous les attributs ont des
valeurs de chaîne simples,
on peut utiliser par commodité les méthodes d'accès direct à la valeur
d'attribut en toute sécurité.
III.
Un exemple simple
-
Objectif
Il s'agit de transformer des fichiers XML représentant des listes de
noms et de notes,
conformément à une discussion
présentée au cours Traductique-1,
- du format (ii) [représentation par attributs, ci-après format
1]
- au format (iii) [représentation par
enfants, ci-après format 2].
Ces deux formats répondent aux deux DTDs données
au cours Traductique-2.
Exemple en
format 1, exemple en format 2.
Ils ont été l'objet de transformations vers HTML en XSLT dans la page de cours sur XSLT.
Un conseil pratique : les fichiers XML produits par les transformations
ci-après
ne contiennent point de sauts de lignes ni de blancs réalisant une mise
en page
pour une lecture commode sous éditeur de texte.
Vous les lirez confortablement avec Firefox !
Pour lire avec profit le code Java, ouvrez le bréviaire dans une autre fenêtre.
-
Réalisations comparées en Perl, PHP-5 et Java
- De 1 vers 2 en Perl : fichier
UnVersDeux.pl
- De 1 vers 2 en PHP-5 : fichier
UnVersDeux.php
Très voisin de Perl, naturellement, mais avec quelques différences.
Attention ! en raison de l'intimité qui règne entre PHP et le serveur Apache,
afin d'assurer sa transmision, ce fichier porte une extension postiche ".txt".
Supprimez-la avant de l'exécuter en ligne de commande !
- De 1 vers 2 en Java : fichier
UnVersDeux.java
Très différent !
En raison du contrôle de types, et aussi de la standardisation des
opérations annexes.
- Variation : transformation inverse, de 2 vers 1.