Présentation d'ECMAScript for XML (E4X) avec Rhino

Christophe Vaudry

ECMAScript for XML (E4X) est une extension de ECMAScript qui ajoute un support natif du langage XML. E4X est un peu à ECMAScript ce que SimpleXML est à PHP5 : il fournit aux développeurs une alternative simple à DOM pour accéder et manipuler des documents XML.

Cet article présente E4X à travers l'utilisation de Rhino, une version de JavaScript conforme aux standards ECMA implémentée en Java. C'est ainsi également l'occasion d'introduire Rhino, à travers l'utilisation d'E4X sur des exemples simples. ?

- Titre : Présentation d'ECMAScript for XML (E4X) avec Rhino

- Auteur : Christophe Vaudry

- Parution : 25 octobre 2007

- 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 ® 2007 cvaudry. 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

- Présentation générale

- I. Installation de Rhino

- II. Un premier contact avec Rhino

- II-A. Un premier exemple simple

- II-B. Un deuxième exemple

- II-C. Utilisation d'une API Java en Rhino

- III. Utilisation d'E4X avec Rhino

- III-A. Manipulation d'un fichier XML Simple

- III-B. Un fichier XML un peu plus complexe

- III-C. Un générateur de volontaires utilisant E4X

- III-D. Un afficheur de citations aléatoires utilisant E4X

- IV. E4X avec Firefox et Seamonkey

- V. Conclusion

- VI. Webographie non exhaustive

- VII. Liens

- VIII. Remerciements

Présentation générale

ECMAScript for XML (E4X) est une extension de ECMAScript/JavaScript qui lui ajoute un support natif de XML. E4X est à ECMAScript/JavaScript ce que SimpleXML est à PHP5 : E4X fournit aux développeurs une manière alternative à l'API DOM d'accéder aux documents XML. Ainsi avec E4X, XML devient un type de base de JavaScript au même titre qu'un entier ou un booléen, facilitant la manipulation de données dans ce format.

Tout comme ECMAScript/JavaScript [1], E4X est standardisé par l'ECMA dans le standard ECMA-357[2]. Néanmoins, E4X est normalement intégré dans JavaScript depuis la version 1.6[3], supportée par Firefox depuis la version 1.5. J'ai pu vérifier qu'E4X fonctionne dans Firefox 2.0 et dans SeaMonkey depuis la version 1.1.2. Il n'est par contre pas supporté dans Internet Explorer 7 et je ne sais pas ce qu'il en est pour les autres navigateurs. La possibilité d'utiliser E4X dans un navigateur est évidemment un point intéressant, notamment dans le cadre d'une utilisation conjointement à l'objet XMLHttpRequest et à XUL[4]. E4X est implémenté dans l'interpréteur JavaScript Rhino[5] écrit en Java. Rhino est orienté pour une utilisation de JavaScript hors du contexte d'un navigateur. Ainsi, il n'implémente pas les objets que l'on trouve habituellement et peut manipuler dans ces derniers. Par contre Rhino peut utiliser nativement des objets issus de classes Java et à l'inverse être intégré (assez) facilement dans un programme Java. Il est prévu à terme que Rhino soit intégré directement dans la JVM des prochaines versions de Java (je crois que c'est même le cas pour la version 6 de Java), au même titre que Groovy ou JRuby. Enfin E4X est a priori implémenté dans ActionScript 3 (ActionScript est également basé sur le standard ECMAScript), mais je n'ai pu vérifier cette information (voir cet l'article[6]).

L'installation de Rhino et les différents programmes d'exemple ont été réalisés sous Windows XP et/ou sous Mac Os X. L'installation et la configuration de Rhino est facilement transposable à un système sous Linux/Unix. Ce qui suit suppose un minimum de connaissances en programmation et en JavaScript ou ActionScript. De toute façon cet article a peu d'intérêt pour vous si vous ne codez pas en JavaScript ou ActionScript. Même si les exemples sont simples, cet article n'est pas un cours d'initiation à JavaScript ou à XML. Mon objectif est de faire découvrir E4X et Rhino au lecteur. Une connaissance minimale de XML (il faut être capable de comprendre un fichier XML bien formé, avoir des notions sur la structure d'arbre d'un document XML, savoir ce qu'est XPath, etc.) est également nécessaire.

I - Installation de Rhino

Rhino est une implémentation d'un interpréteur JavaScript en Java. Il est donc nécessaire d'installer une version récente du JDK si aucune n'est installée sur votre système ou si vous n'avez à votre disposition qu'une version ancienne. Pour utiliser Rhino, il faut le JDK 1.4.2 ou supérieur.

Vous trouverez les dernières versions de Rhino sur le site qui lui est dédié. Les programmes JavaScript de cet article ont été testés avec la 1.6R2 et la 1.6R5. Pour pouvoir exploiter E4X dans Rhino, il est également nécessaire de récupérer XMLBeans (la "binary release" suffit). Les programmes JavaScript de cet article ont été testés avec les versions 2.1.0 et 2.2.0 de XMLBeans. Pour en finir avec les dépendances, XMLBeans a besoin de Saxon. J'ai utilisé la version 8.1.1 dans le contexte de cet article. Rhino, XMLBeans et Saxon sont livrés sous forme de fichier zip qu'il faut décompresser dans des répertoires d'installation correspondants.

Une fois Rhino, XMLBeans et Saxon « installés », il suffit de renseigner correctement différentes variables d'environnement directement au niveau du système ou de prévoir des batchs ou des scripts de lancement pour exécuter Rhino. Ci-après sont proposés trois scripts qui permettent de configurer l'environnement et de lancer Rhino depuis un interpréteur de commande. Il est tout à fait possible d'utiliser l'invite de commande Dos, mais je recommande d'installer et d'utiliser Cygwin sous Windows XP ou Windows 2000, particulièrement si êtes susceptibles de vouloir reprendre tout ceci sous un environnement Unix. Le portage devrait en effet être aisé, Cygwin proposant la plupart des outils et des commandes Shell classiques d'Unix pour Windows. Le script batch pour DOS :

Script batch pour lancer Rhino dans un Shell DOS

set JDK_HOME=C:\APPS\JDK14
set RHINO_HOME=C:\CVApps\rhino1_6R5
set SAXON_HOME=C:\CVApps\saxonb8-1-1
set XMLBEANS_HOME=C:\CVApps\xmlbeans-2.2.0
set RHINO_CLASSPATH=%XMLBEANS_HOME%\lib\xbean.jar;%XMLBEANS_HOME%\lib\xbean_xpath.jar;
set RHINO_CLASSPATH=%RHINO_CLASSPATH%;%XMLBEANS_HOME%\lib\xmlpublic.jar;%XMLBEANS_HOME%\lib\resolver.jar;
set RHINO_CLASSPATH=%RHINO_CLASSPATH%;%XMLBEANS_HOME%\lib\jsr173_1.0_api.jar;%SAXON_HOME%\saxon8.jar;
set RHINO_CLASSPATH=%RHINO_CLASSPATH%;%JDK_HOME%\src.zip;%JDK_HOME%\lib\tools.jar;%JDK_HOME%\jre\lib\rt.jar;
set RHINO_CLASSPATH=%RHINO_CLASSPATH%;%RHINO_HOME%\js.jar
set RHINO=org.mozilla.javascript.tools.shell.Main

%JDK_HOME%\bin\java -cp %RHINO_CLASSPATH% %RHINO% %1

Le deuxième est un script Shell pour l'environnement Cygwin (une bonne alternative pour ceux qui travaillent sous Windows et regrettent leurs outils Unix) :

Script Shell pour lancer Rhino sous Cygwin

#!/bin/sh
echo "Creation des variables d'environnement nécessaire au fonctionnement de Rhino ... "
JDK_HOME="C:\APPS\JDK14"
echo "JDK_HOME : $JDK_HOME"
echo

RHINO_HOME="C:\CVApps\rhino1_6R5"
echo "RHINO_HOME : $RHINO_HOME"
echo

SAXON_HOME="C:\CVApps\saxonb8-1-1"
echo "SAXON_HOME : $SAXON_HOME"
echo

XMLBEANS_HOME="C:\CVApps\xmlbeans-2.2.0"
echo "XMLBEANS_HOME : $XMLBEANS_HOME"
echo

RHINO_CLASSPATH="$XMLBEANS_HOME/lib/xbean.jar;$XMLBEANS_HOME/lib/xbean_xpath.jar;$XMLBEANS_HOME/lib/xmlpublic.jar;"
RHINO_CLASSPATH="RHINO_CLASSPATH;$XMLBEANS_HOME/lib/resolver.jar;$XMLBEANS_HOME/lib/jsr173_1.0_api.jar;"
RHINO_CLASSPATH="RHINO_CLASSPATH;$SAXON_HOME/saxon8.jar;$JDK_HOME/src.zip;$JDK_HOME/lib/tools.jar;
RHINO_CLASSPATH="RHINO_CLASSPATH;$JDK_HOME/jre/lib/rt.jar;$RHINO_HOME/js.jar"
echo "RHINO_CLASSPATH : $RHINO_CLASSPATH"
echo

RHINO=org.mozilla.javascript.tools.shell.Main
JVM="$JDK_HOME/bin/java"
echo "Lancement du shell Rhino"
echo

if [ $# -eq 0 ];
then $JVM -cp $RHINO_CLASSPATH $RHINO;
elif [ $# -eq 1 ];
then $JVM -cp $RHINO_CLASSPATH $RHINO $1;
else echo "Usage : 0 ou 1 parametres en entree :";
echo " -  0 paramètre --> lancement du shell Rhino";
echo " -  1 paramètre --> ce paramètre doit correspondre a un fichier JavaScript a executer";
echo " Vous venez d'appeler le script avec $# parametres en entree";
fi

Le troisième est un script Shell pour l'environnement Mac OS X :

Script Shell pour lancer Rhino sous Mac OS X

#!/bin/bash

echo "Création des variables d'environnement nécessaire au fonctionnement de Rhino ... "

export JDK_HOME="/usr/bin"
echo "JDK_HOME : $JDK_HOME"
echo

export RHINO_HOME="/Applications/JavaAPI/rhino1_6R5"
echo "RHINO_HOME : $RHINO_HOME"
echo

export SAXON_HOME="/Applications/JavaAPI/saxonb8-9-0-2j"
export SAXON_CLASSPATH="$SAXON_HOME/saxon8.jar"
echo "SAXON_HOME : $SAXON_HOME"
echo

export XMLBEANS_HOME="/Applications/JavaAPI/xmlbeans-2.2.0"
export XMLBEANS_CLASSPATH="$XMLBEANS_HOME/lib/xbean.jar:$XMLBEANS_HOME/lib/xbean_xpath.jar:$XMLBEANS_HOME/lib/xmlpublic.jar:$XMLBEANS_HOME/lib/resolver.jar:$XMLBEANS_HOME/lib/jsr173_1.0_api.jar"
echo "XMLBEANS_HOME : $XMLBEANS_HOME"
echo

export JAVALIB_HOME="/System/Library/Frameworks/JavaVM.framework/Versions/CurrentJDK"
export JAVA_CLASSPATH="$JAVALIB_HOME/charsets.jar:$JAVALIB_HOME/dt.jar:$JAVALIB_HOME/jconsole.jar:$JAVALIB_HOME/laf.jar:$JAVALIB_HOME/classes.jar:$JAVALIB_HOME/jce.jar:$JAVALIB_HOME/jsse.jar:$JAVALIB_HOME/ui.jar"

export RHINO_CLASSPATH="$RHINO_HOME/js.jar:$XMLBEANS_CLASSPATH:$SAXON_CLASSPATH:$JAVA_CLASSPATH"
echo "RHINO_CLASSPATH : $RHINO_CLASSPATH"
echo

export RHINO=org.mozilla.javascript.tools.shell.Main
export JVM="$JDK_HOME/java"
echo "Lancement du shell Rhino"
echo

if [ $# -eq 0 ];
then java -cp $RHINO_CLASSPATH $RHINO;
elif [ $# -eq 1 ];
then java -cp $RHINO_CLASSPATH $RHINO $1;
else echo "Usage : 0 ou 1 parametres en entrée :";
echo " -  0 paramètre --> lancement du shell Rhino";
echo " -  1 paramètre --> ce paramètre doit correspondre a un fichier JavaScript à exécuter";
echo " Vous venez d'appeler le script avec $# parametres en entree";
fi

Comme vous l'aurez probablement noté, la structuration des répertoires sous Mac est légèrement différente de celle sous environnement Windows(TM).

Ces trois scripts sont évidemment à personnaliser avant utilisation sur votre environnement. Pour ceux travaillant sous un environnement de type Unix, les scripts Cygwin sont normalement assez directement adaptables. Les principales modifications concernent les variables d'environnement relatives au JDK, à Rhino, à XMLBeans et à Saxon :

- la variable JDK_HOME correspond au répertoire d'installation de votre JDK (pour la version Mac OS X, normalement ce répertoire doit être identique à ce qui est indiqué dans le script) ;

- de même RHINO_HOME, SAXON_HOME et XMLBEANS_HOME correspondent respectivement aux répertoires dans lesquels vous avez placé Rhino, Saxon et XMLBeans ;

- à l'exception de ces modifications, le reste des scripts devraient s'exécuter sans problème.

Pour les trois scripts proposés, le fonctionnement est le suivant :

- si leur appel est effectué sans paramètre, le Shell interactif de Rhino s'ouvre ;

- s'ils sont appelés avec un paramètre correspondant à un fichier JavaScript, ce dernier est exécuté ;

- si vous appelez le premier script (DOS) avec deux paramètres ou plus, seul le premier paramètre sera pris en compte ;

- si vous appelez le second script (Cygwin) avec deux paramètres ou plus, vous aurez un message d'erreur et une explication sur l'usage correct du script.

Pour tester si l'ensemble fonctionne correctement, lancer le script dans un Shell DOS/Cygwin/Mac OS X sans paramètre. L'interpréteur JavaScript de Rhino doit s'ouvrir (vous pouvez en sortir en tapant la commande « quit() ») et vous devez obtenir quelque chose de similaire à l'une des copies d'écran ci-dessous :

Le Shell de Rhino sous DOS

Le Shell de Rhino sous Cygwin

Le Shell de Rhino sous Mac OS X

II - Un premier contact avec Rhino

II-A - Un premier exemple simple

Pour afficher du texte avec Rhino, il suffit d'utiliser la commande print.
Par exemple, pour reprendre l'exemple classique du Hello World!, vous pouvez taper dans l'interpréteur Rhino la ligne suivante :

Hello World sous Rhino...

js> print("Hello World");

JavaScript a une syntaxe proche de celle du C, C++ et Java, même si l'organisation du code est différente de celle d'un programme dans l'un de ces langages.

JavaScript est utilisé principalement comme langage de script associé à une page HTML et est dans ce contexte interprété par un navigateur. Les capacités d'entrée/sorties en mode « console » sont donc assez limitées en JavaScript. Néanmoins, Rhino propose des commandes pour lire un fichier :

- la commande readFile(filename) qui retourne une chaine de caractères correspondant au contenu du fichier.

Les commandes propres à l'interpréteur Rhino peuvent être trouvées sur la page dédiée au Shell de Rhino.

Nous allons commencer par un exemple simple de programme JavaScript composé d'une seule fonction :

fonction citationAlea()

function citationAlea()
{
var citAlea=new Array(); // creation d'un tableau
var indiceCitAlea;
citAlea[0]= "La peur d\'un nom ne fait qu\'accroitre la peur de la chose elle-même. [ J. K. Rowling ]";
citAlea[1]= "Pour un esprit equilibre, la mort n\'est qu\'une grande aventure de plus. [ J. K. Rowling ]";
citAlea[2]= "Les humains ont un don pour desirer ce qui leur fait le plus de mal. [ J. K. Rowling ]";

indiceCitAlea = parseInt(Math.round(Math.random() * (citAlea.length-1)));
print(citAlea[indiceCitAlea]);
}

Vous pouvez taper cette fonction dans votre éditeur de texte préféré et la sauvegarder dans un fichier qui sera placé dans le répertoire dans lequel vous avez lancé l'interpréteur Rhino (pour faciliter les manipulations ultérieures). Vous pouvez également récupérer le fichier citationAleatoire.js.
Quelques éléments d'explication pour ceux qui ne connaissent pas bien JavaScript :

- parseInt est une fonction JavaScript qui convertit (si c'est possible) son paramètre en nombre entier et retourne cette valeur ;

- Math est une classe JavaScript qui contient différentes fonctions mathématiques, dont la fonction random() qui génère un nombre réel aléatoire entre 0.0 et 1.0 (non inclus) ;

- la fonction round() effectue un arrondi à l'entier le plus proche… mais le résultat est un double! Par exemple round(3.7) retournera la valeur 4.0 ;

- pour accéder aux différentes cases d'un tableau on retrouve une syntaxe classique en C, C++ ou Java, la notation crochet : [ ] ;

- les tableaux ont également une propriété length qui permet de connaître leur taille. Cette propriété est accessible en lecture et en écriture en JavaScript ;

- comme en C, C++ et Java, les indices des tableaux commencent à 0.

La fonction est relativement simple : on crée un tableau contenant des citations, on génère un nombre entre 0 et la taille du tableau de citations moins un. Puis, on sélectionne une citation dans le tableau avec ce nombre et on l'affiche. Pour charger la fonction dans Rhino, si le fichier qui la contient est dans le répertoire dans lequel Rhino a été lancé, il suffit de taper :

js> load("citationAleatoire.js");
js> citationAlea(); //Appel de la fonction définie dans citationAleatoire.js

Sinon, si le fichier n'est pas dans le répertoire courant, il faudra indiquer le chemin complet ou relatif du fichier de manière similaire à ce qui est fait ci-après :

js> load("../citationAleatoire.js");
js> citationAlea(); //Appel de la fonction définie dans citationAleatoire.js

Vous devriez voir s'afficher l'une des citations du tableau. Si vous invoquez plusieurs fois de suite la fonction citationAlea(), la citation affichée devrait changer à chaque appel. Ci-après des copies d'écran d'exécution sous l'invite de commande DOS ou sous le Shell de Cygwin.

Copie d'écran du résultat de l'utilisation de citationAlea() (Dos)

Copie d'écran du résultat de l'utilisation de citationAlea() (Cygwin)

II-B - Un deuxième exemple

Examinons une autre fonction, generateurVolontaires(), également simple et tout aussi inutile et qui se trouve dans le fichier generateurVolontaires.js.
C'est un générateur de volontaires qui permet de tirer au sort une personne dans une liste (particulièrement utile aux enseignants quand il souhaite désigner un étudiant volontaire pour une tâche donnée par exemple ! ).

fonction generateurVolontaires()

function generateurVolontaires()
{
/* Création et initialisation de nos variables
liste_personnes est le tableau contenant notre liste de volontaires potentiels
indice_liste est l'indice de l'élément que nous allons "tirer" aléatoirement dans la liste */
var liste_personnes=new Array();
var indice_liste = 0;
liste_personnes[0]= "Terieur Alain";
liste_personnes[1]= "Terieur Alex";
liste_personnes[2]= "Fonsec Sophie";
liste_personnes[3]= "Zeblouse Agathe";
liste_personnes[4]= "Zepower Agathe";
liste_personnes[5]= "Naimarre Jean";
liste_personnes[6]= "Naigrossurlapatate Jean";

/* Choix au hasard  d'un élément de la liste, en générant une valeur d'indice entre 0 et la taille du tableau de personnes
Rappel : en JavaScript comme en C, C++, PHP et Java, les indices d'un tableau de
taille n vont de 0 à n-1 Math.random() génère un nombre réel entre 0 et 1 (non compris)
On génère ensuite un nombre entre 0 et la taille du tableau-1 (non compris)
La fonction round nous permet d'avoir un nombre réel entre 0 et la taille du tableau-1 (inclus cette fois)
le parseInt permet de convertir notre nombre réel en entier.*/

indice_liste = parseInt(Math.round(Math.random() * (liste_personnes.length-1)));
/* Affichage de l'élément sélectionné */
print(liste_personnes[indice_liste]);
}

Une fois cette fonction sauvegardée dans le répertoire idoine, il ne reste plus qu'à charger le fichier dans l'interpréteur et exécuter la fonction :

js> load("generateurVolontaires.js");
js> generateurVolontaires();

Cette fonction est très proche de la précédente, mais au lieu d'avoir un tableau de citations, c'est un tableau de personnes qui est maintenant manipulé.

Copie d'écran du résultat de l'utilisation de generateurVolontaires() (Dos)

Copie d'écran du résultat de l'utilisation de generateurVolontaires() (Cygwin)

II-C - Utilisation d'une API Java en Rhino

Voici un dernier exemple, un peu plus complexe, montrant l'utilisation d'objets Java en JavaScript avec Rhino.
Le programme suivant permet d'utiliser les objets pour créer des interfaces graphiques de Java en JavaScript :

Java et JavaScript font bon ménage avec Rhino...

/* Tout d'abord il nous faut importer les packages Java adéquats :
java.awt
java.awt.event
avec la commande importPackage()
*/
importPackage(java.awt);
importPackage(java.awt.event);

/* Juste une fonction JavaScript qui réalise un affichage de la date courante */
function displayDate () {
print(new Date());
}

/* La fonction "JavaScript" qui créée et affiche la frame */
function createAndDisplayFrame() {

/* Création d'un objet Frame (java.awt.Frame)
On passe en paramètre du constructeur le titre de la Frame */
var frame = new Frame("A Java Frame in JavaScript");

/* On manipule notre objet Java Frame : on fixe sa taille
L'objet Dimension est également un objet Java */
frame.setSize(new Dimension(200,100));

/* Création de 2 boutons (java.awtButton) */
var button_date = new Button("Date");
var button_quit = new Button("Quit");

/* On ajoute un gestionnaire de disposition à la Frame */
frame.setLayout(new FlowLayout());

/* On ajoute les deux boutons à la Frame */
frame.add(button_date);
frame.add(button_quit);

/* On crée deux objets (JavaScript) qui seront appelés lors de l'appui sur les boutons
On notera le nom de la propriété des objets : actionPerformed qui correspond
au nom de la méthode correspondante que doit implémenter un écouteur d'événements
ActionEvent (click sur bouton).
La propriété actionPerformed est associé aux méthodes JavaScript displayDate et quit */
var handler_date = {actionPerformed: displayDate};
var handler_quit  = {actionPerformed: quit};

/* On crée nos écouteurs d'événements à partir des objets avec la propriété actionPerformed
et à partir de l'interface Java ActionListener (java.awt.event.ActionListener) */
var buttonDateListener = ActionListener(handler_date);
var buttonQuitListener = ActionListener(handler_quit);

/* On associe ces écouteurs aux boutons correspondants */
button_date.addActionListener(buttonDateListener);
button_quit.addActionListener(buttonQuitListener);

/* Très important : ne pas oublier de rendre la fenêtre "visible" ! Par défaut elle ne l'est pas !*/
frame.setVisible(true);
}//Fin de la fonction createAndDisplayFrame()

Frame Java créer depuis Rhino

III - Utilisation d'E4X avec Rhino

III-A - Manipulation d'un fichier XML Simple

E4X permet de manipuler du XML directement dans JavaScript. Il est ainsi possible de charger un fichier contenant un document XML et accéder aux différents éléments de ce fichier. On considère le fichier note.xml suivant (ce premier exemple est inspiré et adapté de celui que l'on trouve dans le petit tutoriel sur E4X que l'on trouve sur w3schools)

Fichier note.xml

<?xml version='1.0' encoding='iso-8859-1'?>
<note>
<date>29 mai 2005</date>
<to>Agathe Zeblouse</to>
<from>Agathe Zepoheure</from>
<heading>Message important</heading>
<body>King Kong contre Sauron vient de sortir au cinema</body>
</note>

On considère la fonction JavaScript afficherMessage() suivante du fichier note.js.

fonction afficherMessage()

function afficherMessage()
{
/* Chargement du fichier XML : readFile retourne une chaine de caractères
correspondant au contenu du fichier qui lui est passé en paramètre.
Cette chaine de caractère est stockée dans la variable note */
var note = readFile("note.xml");
/* new XML(note) créer un objet XML en JavaScript à partir de la chaine de caractères
qui lui est passé en paramètre. */
var xmlDoc=new XML(note);
/* typeof retourne le type son paramètre : ici xml */
print("type de la variable xmlDoc : " + typeof(xmlDoc) + "\n");
print("Affichage grace a E4X\n");
/* A partir d'une syntaxe simple, on a accès à tous les éléments du
document XML à partir de la racine du document
xmlDoc.date permet ainsi d'accéder à la valeur contenue dans l'élément
date de la racine note */
print("xmlDoc.date : \n" + xmlDoc.date + "\n");
print("xmlDoc.to : \n" + xmlDoc.to + "\n");
print("xmlDoc.from : \n" + xmlDoc.from + "\n");
print("xmlDoc.heading : \n" + xmlDoc.heading + "\n");
print("xmlDoc.body : \n" + xmlDoc.body + "\n");
print("\nFin du programme\n");
}

Comme précédemment, la fonction peut être chargée et testée dans Rhino comme suit :

Visualisation du contenu d'un fichier XML (note.xml) à l'aide d'une fonction JavaScript

js> load("note.js");
js> afficherMessage();

Le résultat peut être vu dans les deux copies d'écran suivantes (respectivement avec DOS et Cygwin) :

Copie d'écran du résultat de l'utilisation de afficherMessage() (Dos)

Copie d'écran du résultat de l'utilisation de afficherMessage() (Cygwin)

Comme on peut le constater dans la fonction afficherMessage(), avec E4X on peut manipuler particulièrement simplement du XML en JavaScript.
Avec E4X, XML est un type de base de JavaScript. À la ligne :

var xmlDoc=new XML(note);

On crée avec cette ligne une variable qui contient du XML. On fait appel ici au constructeur qui prend en paramètre une chaine de caractères s'évaluant comme du XML bien formé.
Nous verrons un peu plus loin que comme pour les chaines de caractères on peut directement affecter du « XML » dans une variable.
Une fois la variable XML créée et initialisée on accède facilement aux éléments de l'arbre XML. En effet, on accède aux éléments de notre arbre XML avec une syntaxe similaire à celle utilisée pour accéder aux propriétés d'un objet JavaScript, à l'aide de la syntaxe « . » au lieu d'une syntaxe XPath (qui n'est pas supportée par E4X) et qui est classiquement utilisée pour sélectionner les nœuds d'un arbre XML.

III-B - Un fichier XML un peu plus complexe

On considère maintenant le fichier note_2.xml suivant :

fichier XML note_2.xml

<?xml version='1.0' encoding='iso-8859-1'?>
<note>
<date>29 mai 2005</date>
<to>
<prenom>Agathe</prenom>
<nom>Zeblouse</nom>
</to>
<from>
<prenom>Agathe</prenom>
<nom>Zepoheure</nom>
</from>
<heading>Message important</heading>
<body>Les XMen 3 viennent de sortir au cinema</body>
</note>

Considérons la fonction JavaScript afficherMessage_2() suivante qui se trouve dans le fichier note_2.js.

fonction afficherMessage_2()

function afficherMessage_2()
{
var note = readFile("note.xml");
var xmlDoc=new XML(note);
print("type de la variable xmlDoc : " + typeof(xmlDoc) + "\n");
print("Affichage grace a E4X\n");
print("xmlDoc.date : \n" + xmlDoc.date + "\n");
print("xmlDoc.to : \n");
/* xmldoc.to.prenom permet d'accéder au contenu de l'élément <prenom>  qui est défini
dans l'élément <to> lui-même défini dans l'élément racine <note> */
print(">>>> prenom : " + xmlDoc.to.prenom + "\n");
print(">>>> nom : " + xmlDoc.to.nom + "\n");
print("xmlDoc.from : \n" + xmlDoc.from + "\n");
print(">>>> prenom : " + xmlDoc.from.prenom + "\n");
print(">>>> nom : " + xmlDoc.from.nom + "\n");
print("xmlDoc.heading : \n" + xmlDoc.heading + "\n");
print("xmlDoc.body : \n" + xmlDoc.body + "\n");
print("\nFin du programme\n");
}

III-C - Un générateur de volontaires utilisant E4X

Voici un autre exemple qui va mettre à profit la structure de contrôle « for each » de JavaScript : elle permet ici de décrire tous les éléments d'un objet JavaScript. Considérons le fichier personnes.xml suivant :

fichier XML personnes.xml

<?xml version="1.0" encoding="ISO-8859-1"?>
<personnes>
<personne>
<nom>Terieur</nom>
<prenom>Alex</prenom>
</personne>
<personne>
<nom>Terieur</nom>
<prenom>Alain</prenom>
</personne>
<personne>
<nom>Zeblouse</nom>
<prenom>Agathe</prenom>
</personne>
<personne>
<nom>Fonsec</nom>
<prenom>Sophie</prenom>
</personne>
<personne>
<nom>Naigrosurlapatate</nom>
<prenom>Jean</prenom>
</personne>
</personnes>

Puis considérons le fichier JavaScript « personnes.js » suivant :

fonction afficher_personnes()

function afficher_personnes()
{
var personnes = readFile("personnes.xml");
var xmlDoc=new XML(personnes);
var count = 0;
for each (var i in xmlDoc.personne)
{
count ++;
print(" - Personne numero " + count + " est : \n");
print("   >>> Prenom : " + i.prenom + "\n");
print("   >>> Nom : " + i.nom + "\n");
}
print("\n --- nombre d'elements dans l'objet personnes : " + count + " --- \n");
print("\nFin du programme\n");
}

Ce programme nous donne l'affichage suivant :

Copie d'écran du résultat de l'utilisation de afficher_personnes() (Max OS X)

Dans ce code on utilise un compteur (variable « count ») pour comptabiliser au fur et à mesure de la boucle le nombre de nœuds « personne » dans l'objet xmlDoc. La boucle en question itère sur le tableau de propriétés « personne » de l'objet xmlDoc : dans le « for each » la variable i décrit chaque nœud « personne » de la structure XML originale, qui correspond dans l'objet xmlDoc à un objet lui-même de type XML.

III-D - Un afficheur de citations aléatoires utilisant E4X

Les objets de types XML sont manipulables de la même manière que tout autre objet JavaScript : les propriétés de l'objet correspondent aux différentes balises contenues dans la balise racine du document XML initial. Ces propriétés sont elles-mêmes des objets de type XML qui peuvent à leur tour contenir des propriétés de type XML correspondant à des nœuds de l'arbre XML original. Un objet XML en JavaScript reproduit une structure d'objets qui correspond à la structure de l'arbre XML d'origine. Les noms des balises correspondent au nom des propriétés. Ainsi, dans exemple ci-dessous, la propriété « citation » de l'objet XML citAlea correspond à un tableau de 5 objets « citation » possédant une propriété « texte » et une propriété « auteur ». On accède aux différentes valeurs stockées dans l'arbre XML via le nom de la balise correspondante.
Voici un dernier exemple qui reprend le générateur de citation aléatoire vu précédemment.
Cependant, cette fois-ci les citations sont récupérées dans un fichier XML tel que le fichier citations.xml.

fichier XML citations.xml

<?xml version="1.0" encoding="ISO-8859-1"?>
<citations>
<citation>
<texte>La peur d'un nom ne fait qu'accroitre la peur de la chose elle-même</texte>
<auteur>J. K. Rowling</auteur>
</citation>
<citation>
<texte>Pour un esprit équilibre, la mort n'est qu'une grande aventure de plus.</texte>
<auteur>J. K. Rowling</auteur>
</citation>
<citation>
<texte>Les humains ont un don pour désirer ce qui leur fait le plus de mal.</texte>
<auteur>J. K. Rowling</auteur>
</citation>
<citation>
<texte>Ce sont nos choix qui montrent ce que nous sommes vraiment, beaucoup plus que nos aptitudes.</texte>
<auteur>J. K. Rowling</auteur>
</citation>
<citation>
<texte>Je ne suis pas d'une beauté suprême. Mais faut pas s'fier a ce qu'on voit.</texte>
<auteur>J. K. Rowling</auteur>
</citation>
</citations>

Voici une fonction JavaScript décrite dans le fichier(citationAleatoireXML.js) qui permettra d'afficher une des citations choisies aléatoirement dans le document XML :

fonction citationAleaXML()

function citationAleaXML()
{
var chaine = readFile("citations.xml");
var citAlea=new XML(chaine);
var indiceCitAlea = 1;

var count = 0;
for each (var i in citAlea.citation)
{
/* On "parcourt" l'objet pour comptabiliser le nombre de citations
Afin d'avoir la borne haute pour la génération de notre nombre aléatoire */
count ++;
}

/* Même principe que dans la première version du premier "générateur" de citation aléatoire */
indiceCitAlea = parseInt(Math.round(Math.random() * (count -1)));

/* il y a plusieurs balises <citation> dans le document XML (5 dans notre exemple)
donc au lieu d'avoir une propriété, nous avons un tableau de propriétés
Pour accéder à l'une des citations en particulier, on précise un indice à ce tableau */
print(citAlea.citation[indiceCitAlea].texte + " - " + citAlea.citation[indiceCitAlea].auteur);
}

IV - E4X avec Firefox et Seamonkey

Nous allons adapter le programme de la citation aléatoire pour le faire fonctionner sous Firefox 2.0. En théorie, E4X est censé être supporté depuis la version 1.5 de Firefox, mais les essais effectués en ce sens n'avaient pas été concluants à l'époque. Par contre, cela fonctionne sans problème depuis la version 2.0 ainsi qu'avec les dernières versions de Seamonkey (la version utilisée lors de mes tests était une 1.1.2), également issu du projet Mozilla.
Quant à Internet Explorer, cela ne semblait pas fonctionner lors de mes essais avec un IE version 7.0. Ce script ne fonctionne pas non plus avec Opera (version 9.23). Le code JavaScript a été intégré à une page (X)HTML basique. Voici le code de la page :

Illustration du fonctionnement d'E4X avec Firefox

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<title>Essai E4X avec Firefox</title>
<script language="javascript" type="text/javascript">
var chaine = <citations>
<citation>
<texte>La peur d'un nom ne fait qu'accroitre la peur de la chose elle-même</texte>
<auteur>J. K. Rowling</auteur>
</citation>
<citation>
<texte>Pour un esprit équilibré, la mort n'est qu'une grande aventure de plus.</texte>
<auteur>J. K. Rowling</auteur>
</citation>
<citation>
<texte>Les humains ont un don pour désirer ce qui leur fait le plus de mal.</texte>
<auteur>J. K. Rowling</auteur>
</citation>
<citation>
<texte>Ce sont nos choix qui montrent ce que nous sommes vraiment, beaucoup plus que nos aptitudes. </texte>
<auteur>J. K. Rowling</auteur>
</citation>
<citation>
<texte>Je ne suis pas d'une beauté suprême. Mais faut pas s'fier à ce qu'on voit.  </texte>
<auteur>J. K. Rowling</auteur>
</citation>
</citations>;

function citationAleaXML()
{
var citAlea=new XML(chaine);
var indiceCitAlea = 1;

var count = 0;
for each (var i in citAlea.citation)
{
/* Comme dans l'exemple pour Rhino, on "parcourt" l'objet pour comptabiliser le nombre de citations
Afin d'avoir la borne haute pour la génération de notre nombre aléatoire */
count ++;
}

/* Toujours le même principe */
indiceCitAlea = parseInt(Math.round(Math.random() * (count -1)));

/* il y a plusieurs balises <citation> dans le document XML (5 dans notre exemple) ...
donc au lieu d'avoir une propriété, nous avons un tableau de propriétés
Pour accéder à l'une des citations en particulier, on précise un indice à ce tableau
Cette fois-ci, au lieu d'afficher directement la valeur, on la retourne */
return(citAlea.citation[indiceCitAlea].texte + " - " + citAlea.citation[indiceCitAlea].auteur);
}
</script>
</head>
<body>
<h1>Test d'E4X avec Firefox</h1>
Vous devriez voir apparaitre ci-dessous une nouvelle citation à chaque chargement de la page : <br/>
<blockquote>
<script language="javascript" type="text/javascript">
document.write(citationAleaXML());
</script>
</blockquote>
</body>
</html>

V - Conclusion

Ce tutoriel avait comme objectif de présenter Rhino et d'introduire E4X. E4X est une spécification de l'ECMA pour ajouter un support en natif du XML à JavaScript. Nous avons pu constater à travers quelques exemples qu'E4X permet de manipuler simplement XML en JavaScript.

E4X est implémenté dans les navigateurs Firefox et Seamonkey, on peut gager qu'il le sera rapidement dans les autres navigateurs du marché. E4X est implémenté dans Rhino et dans la version 6 de Java qui intègre cet interpréteur.

Avec l'omniprésence de XML dans les développements informatiques à l'heure actuelle, on ne peut que se réjouir de l'existence d'outils pour manipuler programmatiquement ce dernier. De plus, avec le buzz actuel autour d'AJAX, on voit tout de suite l'intérêt qu'il peut y avoir de pouvoir facilement manipuler des données en XML en JavaScript en liaison avec l'objet XMLHttpRequest.

VI - Webographie non exhaustive

- Un tutoriel de W3Schools sur E4X.

- E4X sur Mozilla Developper Center en français et en anglais.

- E4X sur XULfr.org.

- Un court tutoriel de John Udell (en anglais).

- E4X sur Wikipedia en français et en anglais.

- E4X sur faqts.

- Les slides de Brendan Eich (Mozilla) sur E4X.

- Un article : E4X: Une nouvelle approche des manipulations XML sur E4X avec ActionScript 3.

- Une entrée sur le blog de Sylvain Wallez sur la substitution de la NPL au profit de la MPL comme licence de Rhino.

VII - Liens

[1] EcmaScript : Ecma-262 (http://www.ecma-international.org/publications/standards/Ecma-262.htm)
[2] Ecma-357 (http://www.ecma-international.org/publications/standards/Ecma-357.htm)
[3] Javascript 1.6 (http://developer.mozilla.org/en/docs/New_in_JavaScript_1.6)
[4] XUL (http://fr.wikipedia.org/wiki/XUL)
[5] Rhino (http://www.mozilla.org/rhino/)
[6] E4X dans ActionScript 3 (http://weblog.shaoken.be/index.php?2006/04/24/54-e4x-une-nouvelle-approche-des-manipulations-xml)
[7] Java Development Kit (http://java.sun.com/javase/downloads/index.jsp)
[8] Cygwin (http://www.cygwin.com/)
[9] Page Shell de Rhino (http://www.mozilla.org/rhino/shell.html)
[10] SeaMonkey (http://www.mozilla.org/projects/seamonkey/)

VIII - Remerciements

Je souhaite remercier Guillaume Rossolini qui m'a initialement proposé de rejoindre developpez.com ainsi que Mathieu Lemoine et Denis Cabasson pour leur aide et leurs conseils dans la rédaction de ce premier article sur ce site. Un grand merci à Aspic pour sa relecture orthographique. Je remercie une deuxième fois chaudement denisc qui a vraiment beaucoup contribué à la finalisation de cet article.