[AS3] E4x - Du changement au niveau du format XML en AS3.
Par eKameleon le lundi, août 7 2006, 17:05 - AS3 - Flex - Air - Lien permanent
Généralités
Le XML(Extensible Markup Language) est devenu incontournable, c'est un standard du W3C et son utilisation basée sur un format texte utilisant essentiellement des balises permet à l'heure actuelle à de nombreux développeurs de structurer des formats de données qui leur permettrons d'étendre les possibilités dynamiques de leurs applications.
Je vais passer rapidement sur la présentation de cet "outil" et je vais également éviter de vous parler de mes considérations sur l'intérêt d'utiliser ce format par rapport à un format plus proche du langage ECMAScript qu'utilise l'ActionScript... malgré tout l'AS3 dans son lot de nouveautés une approche plus standardisée avec l'E4X une extension du langage ECMAScript utilisant le standard ECMA 357. Nous pouvons dire que cette technique est plus proche d'une programmation orienté objet et permet d'apprécier plus finement l'utilisation du format XML.
A noter que pour la suite de l'article, j'utilise le document XML suivant :
geshi xml <root> <!-- An E4x test --> <?config url="config/config.eden" type="eden" ?> <?locale path="locale/" default="fr" type="eden" ?> <users project="solo" url="http://www.solo.burrrn.com" description="solo is an application framework for ECMAScript 4" > <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> </users> <blackList> <user pseudo="ekameleon"/> </blackList> </root>
En AS1 et AS2 nous avions les classes XMLNode et XML mais leur utilisation était souvant lourde. En AS3 ces classes sont remplacées par les classes XML, XMLList, QName, and Namespace.
A noter que l'ancienne classe XML est toujours présente en AS3 mais Adobe a décidé de renommer cette classe XMLDocument. Nous pouvont la retrouver dans le package flash.xml du framework AS3 avec les classes XMLNode, XMLParser et XMLTag.
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 :
- E4X : Une nouvelle approche des manipulations XML. (Shaoken)
- AS3 - E4X (liguorien)
- ActionScript 3 - E4X (Sephiroth)
- ECMAScript pour XML (Mozilla Developer Center)
- Working with XML (LiveDoc Adobe)
- Using XML Namespaces with E4X and ActionScript3 (Darron schall)
Commentaires
Outch \o/
Ca promet. Je suis assez impressionné par la récupération des données.. c'est pas trop tôt =)
Merci
> je vais également éviter de vous parler de mes considérations sur l'intérêt d'utiliser ce format par rapport à un format plus proche du langage ECMAScript qu'utilise l'ActionScript
Ouais tu auras au moins essayer de pas nous parler d'eden ^^ (changeras jamais)
Difficile de ne pas imaginer EDEN en natif avec la méthode toSource() sur tous les objets .. à mon avis Eden peut être utilisé également avec l'E4X en interne dans le DOM XML, il serait ainsi possible de désérialiser/sérialiser les données qui pour le moment restes au type String et ainsi de garder encore plus profondément le côté ECMAScript de ce format, à voir... :happy:

EKA+
d'abord bravo eka pour cette excellent intro a E4X
en fait c'est simple si on compare ECMAScript+E4X par rapport à d'autres languages soit-disant plus évolués
on voit que:
- le xml est traité comme un litteral
oui ok ca peut foirer un peu la colorisation syntaxique
mais surtout ca permet de traiter un XML litteral comme une string pour la concatenation
voir la doc as3
+ concatenation (XMLList) operator
+= concatenation assignment (XMLList) operator
- E4X explose Xpath et consors en terme d'intuitivité
oui contains() c'est pratique
normalize() et replace() c'est excellent aussi
- ca va vite, tres tres vite
a parser, a traiter, a extraire et j'en passe...
reste que une notation XML et une notation JS (comme avec eden) ca ne sert pas le meme but :P
XML -> structure de document(s)
eden -> structure de donnée(s) typées
par ex en ce moment je prends pas mal la tete pour comment configurer dynamiquement solo
si on prends le modele de .NET ce serait comme ca:
<configuration>
<system.diagnostics>
<switches>
<add name="General" value="4" />
<add name="Data" value="1" />
</switches>
</system.diagnostics>
</configuration>
et humm perso je trouve que qlqlch comme ca serait plus lisible:
Config.trace.general = 4;
Config.trace.data = 1;
et si on utilise un format JS et que donc on a du typage ca permet de noter cette config aussi comme ca
Config.trace.general = TraceLevel.debug;
Config.trace.data = "error";
ewt au final avoir un fichier "Config.eden"
qui contiendra
---------
trace.general = TraceLevel.debug;
trace.data = TraceLevel.error;
---------
bon j'arrete là sinon je vais encore poster trop long,
mais passer par une notation JS a de nombreux avantages: le typage, une structure objet qui peut "fusionner" avec les objets de l'appli, la sécurité du parsing, etc.
Tout à fait d'accord sur la notation Eden dans les fichiers de configuration. A mon avis l'E4X, comme tu le dis n'a pas le même but et possède des arguments de poids dans son domaine par rapport au XPath justement.
merci de ces infos eka.
Mais j'ai une petite question !!!
Il est possible de chargé dynamique un fichier XML dynamiquement, de la modifier dans le swf mais comment faire pour ensuite enregistrer ces modifications ?
Manny
Hello

En principe, si tu es online comme dans les autres versions d'actionscript il faut utiliser un script PHP ou autre, ou alors en offline tu peux utiliser un projecteur comme Zink ou Mprojector (ou toute solution basée sur un activeX en .NET, JAVA, etc....
EKA+
Hello,
Petite question, simple curiosité.
Dans le code suivant (qui se trouve dans la fonction onComplete après le loading du XML) :
xml = new XML(loader.data);
Pourquoi instancier XML au lieu de juster "caster" ce dernier comme ceci :
xml = XML(loader.data);
Je sais que le résultat est le même, j'ai testé... mais ça me perturbe de voir les deux méthodes sans vraiment savoir si y en a une mieux que l'autre en terme de "best practice", au delà de la différence synthaxique.
S.
Hello

Disons que tout dépend si tu reçois une chaine de caractère de ton serveur ou un objet directement avec le type XML.
En principe le fait d'écrire new XML( strXML:String ) permet de s'assurer que l'on parse bien la chaine récupérée en XML.
Après si le type casting permet en AS3 de transformer le résultat du chargement dynamique directement en XML... autant utiliser ce dernier (la notation avec le new XML() reste une habitude AS2/AS1...
EKA+
Ok merci pour les infos.
S.
merci pour cet article il m'as bien aidé merciiiiiii