Déclaration d'une instance de la classe XML

La grosse nouveauté du XML basé E4X c'est la possibilité d'utiliser des balises directement dans le code sans passer par une chaine de caractère pour formatter le DOM du XML. Voyons tout de suite comment déclarer un objet simple de type XML en AS3 :

1 - création d'une instance simple en utilisant le constructeur de la classe XML.

var xml:XML = new XML() ;
trace(xml) ;

2 - création d'une instance avec un paramètre de type String (comme en AS1 et AS2).

var xml:XML = new XML( "<item>test2</item>" ) ;
trace(xml) ;

3 - création d'une instance en utilisant paramètre de type XML directement !

var xml:XML = new XML(<item>test3</item>) ;
trace(xml) ;

4 - création d'une instance en utilisant pas le constructeur mais juste l'objet de type XML natif.

var xml:XML = <item>test4</item> ;
trace(xml) ;

La 4ème syntaxe est tout de même beaucoup plus simple que celles du dessus. L'E4X intègre donc comme vous pouvez le voir un nouveau type d'objet XML natif souple d'utilisation mais attention il peut arriver que son utilisation directe pose un petit problème ... pas un très gros ... juste une petite erreur du colorisateur syntaxique dans Flex2 si vous utilisez des caractères spéciaux comme les commentaires // ou /* ou des caractères " ou ' qui introduisentt l'ouverture d'une chaine de caractère... Un exemple sera plus clair à mon avis pour illustrer ce petit détail :

var xml:XML ;
 
xml = <item>http://www.google.fr</item> ;
trace(xml) ;
 
xml = <item>attention au caractere ' ou ".</item>
trace(xml) ;

Aucune erreur du compilateur bien entendu mais un vrai chaos au niveau de la présentation de votre code ! Pour ma part, je solutionne ce problème en ajoutant la valeur via une variable externe et la méthode appendChild ou alors en utilisant l'ancienne notation avec un constructeur utilisant un paramètre de type String, comme ceci :

var content:String = "http://www.google.fr" ;
 
var xml:XML ;
 
xml = <url/>
xml.appendChild(content) ;
 
trace(xml) ; // sortie : http://www.google.fr
 
xml = new XML("http://www.google.fr") ; // pas de probleme non plus
trace(xml) ;

C'est forcément un peu moins souple que l'utilisation directe mais au moins j'ai plus de problème sur la colorisation de mon code... je comprends bien que Adobe n'est pas prévu de caratère d'échappement pour les objets natifs XML mais je pense qu'un petit coup d'oeil dans l'algo du colorisateur syntaxique pour cette situation ne serait pas un mal ! :)

Vous avez surement remarqué que lorsque vous faites un trace() sur un objet de type XML vous avez un accés direct avec la méthode toString() au contenu du noeud principal de l'objet de type XML alors qu'en AS1 ou AS2 vous aviez l'intégralité du DOM XML. Dans la nature objet de l'E4X votre instance se substitue au premier noeud contenu dans votre fichier ou votre document XML. Si vous désirez récupérer le véritable nom du noeud principal il faudra utiliser la nouvelle méthode localName(). Il est possible sinon d'utiliser la nouvelle méthode toXMLString() qui vous renverra l'intégralité du document XML.

var xml:XML = <item>Mon Texte...</item> ;
 
trace("> xml.locaLName() : " + xml.localName()) ; // sortie : item
trace("> toString : " + xml) ;
trace("> toXMLString : " + xml.toXMLString()) ;

Chargement dynamique d'un document XML externe.

Une autre grosse nouveauté de l'AS3 est d'externaliser le chargement des données externes avec la classe flash.net.URLLoader qui remplace à présent les classes LoadVars et XML au niveau du chargement d'une structure de donnée au format texte ou basée sur des variables encodées au format HTML. Cette classe permet également de charger des données binaires. La classe URLLoader possède un système événementiel complet permettant de notifier plusieurs types d'événements (Event.COMPLETE, ProgressEvent.PROGRESS, etc.)

Pour charger une structure XML externe nous allons donc utiliser la classe flash.net.URLLoader et la nouvelle classe flash.net.URLRequest utilisée par URLLoader pour définir toutes les informations utiles pour gérer un chargement externe.

package
{
 
	import flash.display.Sprite;
 
	import flash.net.URLLoader ;
	import flash.net.URLRequest ;
 
	import flash.events.Event ;
 
	public class TestXML extends Sprite
	{
 
		// ----o Constructor
 
		public function TestXML()
		{
 
			// XML.ignoreComments = false ;
			// XML.ignoreProcessingInstructions = false ;
			// XML.ignoreWhitespace = true ;
 
			var url:String = "xml/test.xml" ; // fichier encode en UTF8 contenant le xml defini au debut du tutorial.
 
			var request:URLRequest = new URLRequest( url ) ;
 
			loader = new URLLoader();
			loader.addEventListener(Event.COMPLETE, onComplete);
			loader.load( request ) ;
 
		}
 
		// ----o Public Properties
 
		public var loader:URLLoader ;
		public var xml:XML ;
 
		// ----o Public Methods
 
		public function onComplete(e:Event):void
		{
 
			xml = new XML( loader.data ) ;
 
			try
			{
 
				trace( "Chargement xml terminé : " + xml ) ;
 
			}
			catch(e:Error)
			{
				trace( e.toString() ) ;			
			}
 
 
		}
 
	}
}

Vous remarquez surement que j'utilise une exception avec un try..catch pour intercepter en cas de problème une erreur lors de l'utilisation de la variable xml une fois le chargement du fichier externe terminé. En effet, en AS3 il n'existe plus comme en AS1 ou AS2 de propriété status pour définir si le xml est valide ou non. L'AS3 utilise maintenant des erreurs internes pour notifier le statut de la structure de donnée. Je vous conseille de toujours travailler dans une instruction try..catch pour éviter les coupures de code lors du debug de votre application en cas de problème, c'est plus simple ensuite pour corriger ce qu'il ne va pas.

Types de balises

Si vous êtes un peu habitué au format XML vous devez reconnaitre facilement la différence entre une balise contenant du texte, contenant des attributs, contenant des commentaires, des balises de type CDATA etc... Dans l'exemple au début tutorial vous pouvez observer divers types de balises. J'ai surtout mi en avant 2 types de balise importants avec les balises de type "Commentaires" et les balises de type "Processing". Voyons de plus prêt ces balises :

geshi xml
<root>

	<!-- An E4x test -->

	<?config url="config/config.eden" type="eden" ?>

	<?locale path="locale/" default="fr" type="eden" ?>

        etc...

Les balises <!-- --> sont des balises permettant de définir des commentaires dans le document XML. Ces balises pourront être utilisées en interne dans le document sans être interprété par le parseur E4x.

Les balises <? ?> sont les balises de processing, elles sont utilisées pour lancer des instructions précises lors du parcours du document XML. On peut les considérer comme des balises d'initialisations personnalisables qui vous permettent de définir toute sorte de fonctionnalité (définir la feuille de style à charger pour afficher le contenu texte du document XML, initialiser et configurer l'application, établir un protocole de localisation etc.). Pour le moment je considère qu'il serait tout à fait possible d'utiliser des balises classiques pour définir ce genre d'information mais l'intérêt ici est le même que les balises de commentaires car en effet il est possible d'empêcher le compilateur d'interpréter ce genre de balise.

Pour contrôler l'utilisation de ces balises il existe 3 nouvelles propriétés statiques liées à la classe XML qui vont vous permettre à tout moment de définir la manière dont vous voulez interpréter votre document XML :

  • XML.ignoreComments:Boolean : permet d'ignorer ou non les commentaires dans le document XML.
  • XML.ignoreProcessingInstructions : permet d'ignorer ou non les instructions de type processing contenues dans le document XML.
  • XML.ignoreWhiteSpace : remplace l'ancienne propriété de la classe XML (AS1 et AS2) ignoreWhite. Ici le changement vient de l'apparition d'une propriété statique et non plus liée aux instances directement.

Dans l'exemple d'utilisation de la classe URLLoader au dessus, je vous propose d'enlever les commentaires dans le constructeur de la classe TestXML et ainsi de tester de plus prêt les nouvelles propriétés statiques définies ci-dessus.

A noter que par la suite une fois le document XML chargé, qu'il est possible d'utiliser les méthodes comments() et processingInstructions() pour récupérer respectivement les XMLList des balises de type "comments" et "processing".

trace("xml.comments : " + xml.comments()) ;
 
 
trace("------- Processing Instructions") ;
 
var allPI:XMLList = xml.processingInstructions() ;
 
trace("count Processing Instructions in xml : " + allPI.length() ) ; // nombres de balises de type "processing"
 
trace("xml.processingInstructions('config') : " + xml.processingInstructions("config")) ; // obtenir la balise "processing" ayant pour nom 'config'
 
trace("xml.processingInstructions('locale') : " + xml.processingInstructions("locale")) ; // obtenir la balise "processing" ayant pour nom 'locale'

Attention l'utilisation d'un trace sur la variable allPI ci-dessus ne renvoi aucun message dans le panneau de sortie, même si la liste contient bien plusieurs balises. Je n'ai pas encore trop chercher à comprendre comment fonctionne la classe XMLList mais là dessus c'est un peu étrange d'avoir une sortie pareille pour un début sur ce type de balises.

Remarque : pensez à initialiser correctement les propriétés XML.ignoreComments et XML.ignoreProcessingInstructions (false pour pouvoir accéder aux balises).

Affichage et debug d'un objet de type XML.

Nouveauté également au niveau du debug d'un objet de type XML avec l'apparition des propriétés statiques XML.prettyIndent et XML.prettyPrinting.

  • XML.prettyIndent : permet de définir via un entier (int) le décalage entre chaque niveau dans l'arborescence du document XML.
  • XML.prettyPrinting : permet d'appliquer un formattage ou non sur le document XML lors de l'utilisation de la méthode toString() ou de la méthode toXMLString().
XML.prettyIndent = 2 ;
trace(xml) ;
 
trace("----") ;
 
XML.prettyIndent = 2 ;
trace(xml) ;
 
trace("----") ;
 
XML.prettyPrinting = false ;
trace(xml) ;

Autre amélioration avec l'apparition de la méthode XML.defaultSettings() qui renvoie un objet contenant les propriétés par défaut des documents XML.

var defaultString:Object = XML.defaultSettings () ;
 
for (var prop:String in defaultString)
{
      trace( " > " + prop + " : " + defaultString[prop]) ;
}
 
/* Sortie :
 
 > prettyPrinting : true
 > prettyIndent : 2
 > ignoreComments : true
 > ignoreProcessingInstructions : true
 > ignoreWhitespace : true
 
*/

Ce qui est intéressant, c'est qu'il est possible de créer son propre profile pour définir les propriétés de la classe XML et d'en changer à tout moment avec la méthode XML.setSettings(). La méthode XML.settings() renvoie l'objet contenant les propriétés courantes de la classe XML.
Si vous désirez à tout moment revenir aux propriétés par défaut, il suffit d'écrire dans votre code :

XML.setSettings(XML.defaultSettings());

Récupérer les données contenues dans un document XML

Comme je l'ai déjà dit, l'E4X propose une approche objet beaucoup plus intuitive pour parcourir rapidement le contenu d'un document XML et pour en récupérer le contenu. Voyons maintenant de plus prêt les mots clés et les instructions qui vous vous permettre d'utiliser au mieux votre document.

En regardant de plus prêt la structure du XML fourni au début du tutorial regardons comment nous allons récupérer le contenu de la balise <age> du premier <user> contenu dans le document XML.

En AS2
var age:String = x.firstChild.firstChild.firstChild.firstChild.firstChild.nodeValue ;
trace("> " + age) ; // sortie : > 29
En AS3 :
var age:String = xml.users.user[0].age ;
trace("> " + age) ; // sortie : > 29

Il est évidant que la syntaxe en AS3 est beaucoup plus souple que celle en AS2 (ou AS1 c'est pareil).

Les noeuds enfants deviennent en AS3 des propriétés du noeud parent

trace( xml.users.user ) ; // user enfant du noeud users.

Voyons un exemple un peu plus complet :

trace("------ XMLList") ;
 
trace("> xml.users.user is XMLList : " + (xml.users.user is XMLList) ) ; 
/* sortie 
 
	> xml.users.user is XMLList : true
 
*/
 
trace("> " + xml.users.user[0].age) ; // sortie : > 29
 
trace("> Toute la liste des balises age contenues dans xml.users :
" + xml.users.user..age) ; // use .. operator
/* sortie 
 
	Toute la liste des balises age contenues dans xml.users :
	<age>29</age>
	<age>29</age>
	<age>23</age>
	<age>29</age>
	<age>32</age>
 
*/
 
trace("> Toutes les balises contenues dans xml.users :
" + xml.users.*) ; // use * operator
/* sortie 
 
	Toutes les balises contenues dans xml.users :
	<user pseudo="ekameleon"><age>29</age><url>http://www.ekameleon.net</url></user>
	<user pseudo="iteratif"><age>29</age><url>http://iteratif.free.fr/blog</url></user>
	<user pseudo="shaoken"><age>23</age><url>http://weblog.shaoken.be</url></user>
	<user pseudo="philippe"><age>29</age><url>http://philippe.elsass.free.fr</url></user>
	<user pseudo="zwetan"><age>32</age><url>http://www.zwetan.com</url></user>
*/
 
trace("> Liste des utilisateurs ayant un age entre 24 et 31 ans :
" + xml.users.user.(age|> > 23 && age < 32) ) ;
/* sortie
 
	> Liste des utilisateurs ayant un age entre 24 et 31 ans :
	<user pseudo="ekameleon"><age>29</age><url>http://www.ekameleon.net</url></user>
	<user pseudo="iteratif"><age>29</age><url>http://iteratif.free.fr/blog</url></user>
	<user pseudo="philippe"><age>29</age><url>http://philippe.elsass.free.fr</url></user>
 
*/
 
trace("------ attributes") ;
 
// utiliser le mot cle @ et l'appel de l'attribut directement.				
trace("xml.users.@project : " + xml.users.@project) ; // syntaxe directe.
 
// utiliser le mot cle @ et la syntaxe entre crochet.
trace("xml.users.@url : " + xml.users.@["url"]) ; // syntaxe entre crochet.
 
// utiliser la methode attribute.
trace("xml.users.attribute('description') : " + xml.users.attribute("description")) ; // 
 
trace("------ methode children()") ;
 
// Nombres de noeud dans le noeud users.
 
trace( "xml.users.children().length() : " + xml.users.children().length() ) ;

Il est vraiment intéressant de noter l'apparition d'une recherche interne dans les noeuds du document xml (avec la méthode contains() ou avec la syntaxe entre parenthèse comme j'ai pu le faire dans l'exemple au dessus). En complétant cette recherche avec des expressions régulières, il est maintenant possible de récupérer comme dans un moteur de recherche les noeuds que vous désirez suivant des critères bien précis !

Les possibilités avec l'E4X comme vous pouvez le voir sont donc nombreuses. A mon avis le mieux, pour bien cerner cette classe (comme beaucoup d'autres), c'est que vous preniez le temps de lire la référence du language et de compléter tout ce que je viens d'écrire en appliquant chaque exemple contenu dans l'aide et cela pour chaque méthode.

Je pense que pour le moment je vais m'arrêter là sur la description des fonctionnalités de cette nouvelle classe XML. Je reviendrai vers vous dans quelques temps pour vous parler entre autre des namespace et de tout ce qui me semblera utile. Je n'ai pas encore pu vraiment apprécier toutes les possibilités du XML en AS3 et j'aimerai prendre un peu de recul pour en savoir plus et surtout pour le comparer avec le format EDEN.

A lire sur le sujet :