Object to XML to Object : conversion XSD


back

Utiliser les schéma de déclaration XSD, s’avère souvent fort utile. Plus que l’autocompletion sur vos fichiers XML, l’apport des XSD se trouve avant tout dans le fait de qualifier et de valider vos fichiers. Dans Flex, bien que caché au fin fond du package rpc, la mécanique de lecture et de conversion XSD est bien là. Nous allons voir comment l’utiliser, et convertir vos objets AS3 vers XML (et vice versa), en suivant ces schémas de définition.

Pourquoi utiliser une XSD?

Pour ma part, ces quelques raisons (et pour vous certainement plein d’autres raisons)

  • Elle valide vos XML : parce que vous définissez en amont la structure de vos fichiers, une définition XSD permet de les valider;
  • Parce que vous avez besoin de plusieurs écritures différentes d’un même jeu de donnés;
  • Parce que, comme tout bon dèv, vous êtes fainéant…
  • …et le fait que le programme valide tout et fait tout, tout seul, en suivant des règles normées ça vous va bien.
  • Ca fait toujours bien d’avoir des fichiers XSD dans vos sources : ça envoie de la rosette, et vous pouvez vous la péter grave.

Dans un usage Flex, l’objectif serait de pouvoir transformer nos objets AS en XML, et l’inverse, via les schémas de définitions. Ça tombe bien ! C’est ce qu’on va faire.

Où tu te caches XSD ?!

Dans la plupart des langages, nous avons la possibilité de discuter avec notre serveur en utilisant le protocole SOAP, basé sur XML. En utilisant ce protocole, nos objets sont “transformés” en XML, et transmis vers notre serveur. L’opération inverse existe lors de réception de données.
Mais pourquoi je vous parle de ça? Tout simplement parce que tout langage faisant du SOAP (ou du web service), à besoin de valider et de convertir de manière automatique les objets. Et je vous le donne en mille, ce sont les définitions XSD qui entrent en jeux (souvent :) ).

La mécanique XSD est utilisée, en interne dans Flex, pour l’encodage et le décodage d’objet SOAP ou WSDL. Ces classes, exclues du framework (vous n’y avez pas accès directement avec l’autocomplétion), se trouvent dans le package mx.rpc.xml.*.

rpcxml

Et c’est bien dommage que ces classes soit exclues ! Selon moi, elles seraient bien plus utiles ouvertes, documentées, et dans un package plus global comme flash.xml par exemple (flash.xsd ou flash.xml.xsd… ça pourrait être cool !).

Car vous allez le voir, il est relativement simple d’utiliser cette partie de Flex, pour un usage personnalisé du XML, en dehors du contexte RPC.

Des groupes, des geeks, des bidules

Donc c’est parti pour l’exemple. Je ne me casse pas la tête, et vous propose un biniou qui aura 3 éléments : des groupes, des gens (geek forcement), et des bidules… Je n’étais pas inspiré sur le 3ème éléments, mais il m’en fallait un….

XML et la définition XSD

Nous avons une infinité d’écriture XML possible, comme celle-ci :

<?xml version="1.0" encoding="UTF-8"?>
<groups>
    <group>
        <groupName>klingons</groupName>
        <geeks>
            <geek>
                <firstName>francis</firstName>
                <stuffs>
                    <stuff>
                        <name>mac</name>
                  </stuff>
                   <stuff>
                        <name>Flex</name>
                    </stuff>
                </stuffs>
            </geek>
        </geeks>
    </group>

    <group>
        ...
</groups>

Ou celle là :

<?xml version="1.0" encoding="UTF-8"?>
<groups>
    <group groupName="klingons">
        <geeks>
            <geek firstName="francis">
                <stuffs>
                    <stuff name="mac" />
                    <stuff name="Flex" />
                </stuffs>
            </geek>
            <geek firstName="roger">
                <stuffs>
                    <stuff name="pc" />
                    <stuff name="photoshop" />
                </stuffs>
            </geek>
        </geeks>
    </group>

    <group>
        ...
</groups>

Ou un mélange à votre sauce, vous faites bien comme vous voulez !

Nous allons donc écrire notre XSD, qui va définir le XML définit ci-dessus, celui qui utilise pleins d’attributs. Je prend celui là parce que la sortie XML est moins longue…. et mon article gagnera quelques lignes…. En ce moment mes articles sont un peu trop pépère… Ca doit être l’approche des fêtes.

Bref !

Le but est de réaliser une XSD la plus complète possible, ou toutes les parties sont typées (SimpleType ou ComplexeType). C’est important pour la suite, car chaque type sera “mappé” avec la classe ActionScript qui lui correspond.

Dans notre exemple, nous avons 3 collections (groups geeks, stuffs) et 3 éléments (group, geek, stuff)… D’accord je ne me suis pas foulé les doigts sur ce coup là !

Le schéma XSD n’est finalement qu’un fichier XML standard :

<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema
    xmlns:xsd="http://www.w3.org/2001/XMLSchema"
    targetNamespace="schemas/geeks"
    xmlns="schemas/geeks">

    <xsd:element name="groups" type="groupsCollectionType" />

</xsd:schema>

L’élément principale, le noeud “<groups>”, est la base de notre schéma. Le type correspondant va s’appeler groupsCollectionType.

La version actionScript de ce type groupsCollectionType serait un Array ou ArrayCollection (ou tout autre type de collection d’objet).
groupsCollectionType est une séquence, un réceptacle de 0 à n éléments de type group (le nombre d’instances minimum et maximun est défini par minOccurs et maxOccurs).

<xsd:complexType name="groupsCollectionType" mixed="true">
    <xsd:sequence>
        <xsd:element name="group" type="groupType"
            maxOccurs="unbounded" minOccurs="0" />
    </xsd:sequence>
</xsd:complexType>

Le type group est composé comme suit :

<xsd:complexType name="groupType" mixed="true">
    <xsd:sequence>
        <xsd:element name="geeks" type="geeksCollectionType"
            minOccurs="0" maxOccurs="1"  />
    </xsd:sequence>

    <xsd:attribute name="groupName" use="required" type="xsd:string" />

</xsd:complexType>

On retrouve le noeud geeks, unique, mais non obligatoire (minOccurs= »0″ maxOccurs= »1″), qui sera défini dans un type geeksCollectionType de la même manière que groupsCollectionType.
Les attributs du noeud sont placé à la suite de la séquence d’éléments. Ils peuvent être requis ou optionnel, définit par l’attribut use. On peut aussi le définir “prohibited” même si je ne vois pas vraiment l’intérêt…

Et on continue comme cela, en rentrant en profondeur dans nos différent objets. La syntaxe n’est pas compliquée, il suffit de se pencher un peu sur les spécifications de la W3C.

Vous trouverez ici la XSD complète de notre exemple.

Côté ActionScript

Nous avons besoin de 3 objets, qui correspondent aux 3 types d’éléments de notre XML : des groupes de Geek, des Geeks, des Stuffs.

Là, rien de plus basique.

  • La classe GeekGroup : avec une propriété groupName, et une collection geeks (les geeks membres du groupe)
  • La classe Geek : avec une propriété firstName, et une collection stuffs (les bidules du geek)
  • La classe Stuff : avec une propriété name, le nom du bidule

Vous vous rendez compte, tout d’un coup, que je ne vous ai absolument rien appris dans ce paragraphe. Passons à la suite.

Les classes de gestion XSD

Nous avons une XSD et des objets AS prêts à être convertis. Regardons de plus près les classes exclues dont nous avons besoin:

  • mx.rpc.xml.SchemaTypeRegistry : cette classe est une sorte de dictionnaire, ou l’on référence les différents types du schéma XSD, avec leurs classes actionScript associées.
  • mx.rpc.xml.XMLDecoder : la classe qui permet de décoder une source XML, en suivant la XSD, vers un object typé actionScript.
  • mx.rpc.xml.XMLEncoder : la classe qui permet d’encoder un objet AS en un fichier XML, en suivant la définition XSD.
  • mx.rpc.xml.Schema : la classe contenant le schema XSD chargé.
  • mx.rpc.xml.SchemaManager : point central du système, cette classe est utilisée par l’encodeur et le décodeur. Elle possède une référence au schéma (elle peut d’ailleurs contenir plusieurs schéma XSD), et utilise les références de classes et de type XSD enregistrés dans mx.rpc.xml.SchemaTypeRegistry.

Votre object en XML, via XSD

Dans un premier temps, il nous faut charger la définition XSD via un URLLoader :

var urlLoader : URLLoader = new URLLoader();
urlLoader.addEventListener( Event.COMPLETE, onLoadXsdComplete );
urlLoader.load( new URLRequest( "geek.xsd" ) );	

A la réception de la XSD, nous initialisons la classe Schema et le SchemaManager :

var _schema:Schema = new Schema();
var _schemaManager:SchemaManager = new SchemaManager();

_schema.xml = XML( urlLoader.data );
_schemaManager.addSchema( _schema );

Nous ajoutons la référence du manager de schéma dans nos objets d’encodage/décodage :

var _xmlDecoder:XMLDecoder = new XMLDecoder();
var _xmlEncoder:XMLEncoder = new XMLEncoder();

_xmlDecoder.schemaManager = _schemaManager;
_xmlEncoder.schemaManager = _schemaManager;

Ensuite, nous référençons les types contenus dans la XSD, avec les classes AS correspondantes :

var _schemaTypeRegistry : SchemaTypeRegistry;
_schemaTypeRegistry = SchemaTypeRegistry.getInstance();

_schemaTypeRegistry.registerClass(
    new QName(_schema.targetNamespace.uri, "groupsCollectionType"), ArrayCollection);
_schemaTypeRegistry.registerClass(
    new QName(_schema.targetNamespace.uri, "groupType"), GeekGroup);

_schemaTypeRegistry.registerClass(
    new QName(_schema.targetNamespace.uri, "geeksCollectionType"), ArrayCollection);
_schemaTypeRegistry.registerClass(
    new QName(_schema.targetNamespace.uri, "geekType"), Geek);

_schemaTypeRegistry.registerClass(
    new QName(_schema.targetNamespace.uri, "stuffCollectionType"), ArrayCollection);
_schemaTypeRegistry.registerClass(
    new QName(_schema.targetNamespace.uri, "stuffType"), Stuff);

Enfin, nous récupérons notre XML tout bien valide, image de l’objet targetObject, via l’encodeur XML (il faut préciser le nom du noeud “root” de la XSD) :

var xmlList : XMLList = _xmlEncoder.encode( targetObject,
    new QName( _schema.targetNamespace.uri, "groups") );

Super simple.

D’un XML vers un Object

C’est magique, c’est tout pareil ! au lieu d’utiliser XMLEncoder, on utilise XMLDecoder, et on change la référence de l’objet par notre XML :

var obj : ArrayCollection = _xmlDecoder.decode( myXml,
    new QName( _schema.targetNamespace.uri, "groups") ) as ArrayCollection;

Automatisation

Je vous propose un set de classes pour faire tout ça de manière automatique.

  • XSDConverter : un convertisseur XML via XSD.
    2 méthodes :

    • importFromXml( xml : *, xsdUrl : String, schemaTypes : Vector. = null ) : void
    • exportToXml( target : Object, xsdUrl : String, schemaTypes : Vector. = null ) : void

    En paramètre de ces méthodes :

    • la source : Object pour l’exportation, une String, un XML ou un XMLList pour l’importation;
    • xsdUrl : l’url de la xsd a charger;
    • schemaType : une collection (Vector) d’objet de type XSDType, définissant le mapping entre les types XSD, et les classes référentes AS.
  • XSDEvent : l’évènement diffusé par XSDConverter (importSuccess, exportSuccess et conversionError (en cas de problème de conversion ou de chargement de la XSD ) ).
  • XSDType : définition d’un type XSD, avec la classe AS associée.

Un petit exemple est disponible là, avec les sources sous ce lien.

Et donc….

… vous pouvez vous dire que “Ahh ! Maintenant que mes données sont automatiquement transformées en XML, je me sens mieux. Et comme j’ai gagné plein de temps, ba du coup je peux lire des articles fort intéressants comme ceux concernant le skinning ou les primitives dans Flex4. De toutes façons y’a trop de monde à la Fnac pour finir mes cadeaux….”


Tags: , ,


12 commentaires ...

» RSS des commentaires
  1. Armetiz /
    -->

    Bonjour,
    Merci bien pour cet article fort intéressant !

    Il est vraiment dommage que Adobe « cache » des fonctionnalités qui peuvent s’avérer très pratique !

    Comme tu dispense de super cours – sisi, j’insiste ! – comment cela fonctionne lorsque l’on souhaite faire des affectations « transverse » ?
    Pour reprendre ton exemple, comment faire pour qu’un Geek soit dans plusieurs Groupes ? Et sous quelle forme cela se traduira au niveau XML ?

    En tout cas, merci pour l’article ;)

  2. switcherdav /
    -->

    Salut,

    Je n’en suis qu’au début de la lecture de cet article et une question me taraude

    Est-ce que tu penses que cette fonctionnalité peut-être utile pour envoyer des infos à un service AMF (amfphp dans mon cas) ?

    Je m’explique, recevoir des objets custom dans flash, je trouve ça génial, en revanche, envoyer des objets custom, ça fonctionne bien, mais pour tester les services dans PHP directement, c’est un peu galère

    Du coup, je me dit que je pourrais toujours manipuler de l’objet custom côté flash sauf que dans le sens flash > amfphp, je pourrais envoyer un XML

    Dès lors, il m’est facile de tester le service PHP en copiant le XML produit dans mon script PHP

    Qu’est-ce que vous en dites docteur ? c’est grave ?

    Au passage, merci de partager des connaissances et pour la qualité de tes posts

  3. Fabien /
    -->

    @Armetiz : On peux imaginer plusieurs solutions… Peut être que le plus simple serait d’avoir Une collection de groupes, et que l’objet Geek référence la liste des groupes auquel il appartient. Du coup 2 objets, et 2 xml. ou alors un objet supplémentaire, avec définition de tous les groupes, et tous les geeks, avec leurs appartenance aux groupes. Et un xml qui ressemblerai à ca:
    <contents>
        <groups>
            <group name= »nomDuGroupe » ref= »group1″ />
            …
        </groups>
        <geeks>
            <geek firstName= »jean claude »>
                <groupsRef>
                    <groupRef>group1</groupRef>
                    …
                </groupsRef>
            </geek>
            …
        </geeks>
    </contents>
    Mais bon… là on met le doigt sur d’autre question :) comment bien architecturer ces données, etc…

    @switcherdav : Je n’y vois pas d’inconvénient ;) Si travailler avec du XML est plus confortable, la conversion par XSD a le gros avantage de valider ton objet. Ton xml répond au « contrat », et tu es sûr d’obtenir la bonne représentation de l’objet, puisque l’encodeur interne ce charge de toutes les validations. Ce qui serait intéressant serait de pouvoir continuer le processus en faisant de même côté PHP. Une version serveur de la conversion XSD, pour transférer le xml modifié, dans l’objet typé AS3. La XSD étant partagé côté client et côté serveur. Je ne suis pas expert PHP, mais on doit pouvoir trouver cette mécanique.

  4. Palleas /
    -->

    J’ai noté l’expression « ça envoie de la rosette! », j’ai un peu l’impression qu’avec toi c’est « ça envoit XXX » avec XXX qui change régulièrement, et c’est bien marrant :D

    Plus sérieusement, je bosse sur une application AIR 2.0 avec un server (new SocketServer() OH YEAH) et un client qui va s’y connecter. Je n’ai pas encore attaqué l’échange à proprement parlé, vu que je ne sais pas quelle solution choisir. En voyant ton article, je me dis que lancer des objets qui sont transformés en XML vers le serveur qui peut récupérer ensuite un objet et répondre avec un objet transformé en XML que le client récupère en objet… Ca peut se tenter non? Après tout, on va juste manipuler des chaines de caractères… Qu’est-ce que t’en penses? :o

    Super article en tout cas, c’est fourbe que Adobe ait un XMLLoader tout prêt si on n’a pas le droit de l’utiliser ouvertement <_<

  5. Fabien /
    -->

    @Palleas : Bien joué l’araignée ! T’as remarqué le XMLLoader : un autre bidule chaud bouillon… Mais si on devais parler de tout, ça ne serait pas drôle ! Et ce blog ressemblerai plus à un bouquin qu’autre chose… déjà que j’aimerais écrire en anglais… quand je serais grand :)
    Pour ton histoire, Ba ouai pourquoi pas ! Ce qui peu être pratique c’est de stocker tes objet localement en XML (si besoin) : ca évite de devoir gérer les limitations de SQLite (dans le cas d’une appli bien débilou bien sur, mais là je te fait confiance). Après, et c’est valable aussi dans le cas de @switcherdav, ce qui pourrait être bien, c’est de faire 2/3 tests de perf entre un objet Object et un objet XML, sérialiser via AMF… Bon là ca commence à sentir le chipotage, surtout que je ne suis pas certain que ca bouffe des millisecondes.

    Mon opinion : moi j’aime bien avoir des objets tout comme je veux, quand je le veux, sans me taper des parseurs et autre lignes de codes relou. J’préfère me prendre la tête sur des notions d’ergo design, c’est plus fun. D’où ces articles d’ailleurs. Si cette technique de conversion vous fait gagner du temps : banco ! Le cas typique d’utilisation est une appli AIR pour de la sauvegarde local, et synchronisme avec des objets Object serveur. Comme tout le monde parle la même langue, c’est impec. Le tout est de bien penser vos schéma XSD. Le côté fun de cette technique est de pouvoir switcher de XSD, et donc d’avoir des formalismes XML différents, qui peuvent varier en fonction de l’évolution de vos projets. monSchema1.0.xsd, monSchema1.1.xsd : on gère mieux le versionning du projet, et on évite des effets de bord super chiant en cas d’évolution importante du système.

    Sinon je suis curieux : y va faire quoi ce socket ??

    Merci pour les retours ! Et soyez bon : faites du Fx4 :)

  6. Palleas /
    -->

    Rien de bien compliqué, mais j’essayerais de te raconter ça directement, la j’avoue que j’ai pas tout mis à plat dans ma tête ^^’

    Merci pour la réponse en tout cas!

  7. Antonin /
    -->

    Super article, comme d’hab j’ai envie de dire :)

  8. Régis /
    -->

    J’ai rien compris mais ça a l’air de déboiter ton astuces, bienvenue dans le monde des geeks, ;)

  9. tonibache /
    -->

    Pourquoi ne pas avoir utilisé SchemaLoader au lieu de URLLoader dans la class XSDConverter ?
    Dans le cas d’include dans le xsd, la classe ne fonctionne pas. URLLoader ne charge pas les autres xsd.

  10. Fabien /
    -->

    @tonibache : très bonne remarque. Je fait une mise à jour au plus vite. Merci pour le retour.

  11. nullPointerException /
    -->

    Bonjour,
    je voulais savoir si vous avez une astuce pour valider un xml par un schéma XSD. Une métohde qui renvoie les lignes erronées par exemple… Ca fait un petit moment que je cherche et je trouve rien, alors please help…

  12. fab /
    -->

    @nullPointerException : Il existe une méthode très simple pour créer et valider des xml qui doivent suivre un formalisme XSD : les WTP de Eclipse. Les WTP ( pour Web Tools Platform ) est un jeu d’outils et de perspectives additionnelles pour le développement Web. Les WTP son largement utilisé par les développeurs Java, notamment pour la perspective J2EE dédié. En ce qui nous concerne, les WTP incluent tous un tas d’outils pour la conception XML/XSD.

    Intallation des WTP:
    dans FB4 (et donc dans Eclipse Galileo) vous devez ouvrir « Help>Install New Software ». Dans le champ « Work with : », sélectionner Galileo.
    Si l’url de l’update site de galileo n’est pas présent, cliquez sur le lien « Avaible Software site », puis dans la fenêtre suivante « Add », et ajouter en Name « Galileo » et dans Location : « http://download.eclipse.org/releases/galileo/ » Puis OK.

    Un fois l’update site Galileo sélectionner, cocher « Web, XML and Java EE Development » ( vous pouvez bien sur ne choisir que la gestion XML, mais le set d’outil proposé est vraiment bien, en incluant tous les outils pour JavaScript, XML, XSL, XSD, HTML, XHTML, CSS, JSP, EJBs, Webservices, et JPA. Tous un programme ). validez tous (certainement avec plein de fenêtre), redémarrez et voila! vous avez la super total.

    Utilisation pour les XML :
    Pour créer un fichier XML suivant une XSD, clic droit sur votre projet puis « New>Other…>déployez XML > XML » (je part du principe que vous êtes sous FB, en perspective Flash…. Mais vous y arriverez très bien sous d’autre configuration :) ). Plusieurs fenêtre vous guide pour créer votre XML, et notamment des options de création suivant une XSD. Vous aurez par la suite l’autocomplétion dans vos XML (classe!) et le suivie d’erreur.

    Je vous invite aussi a regarder les vues « design » de création de fichier XSD, qui vous permet de tomber vos fichier en un temps reccord!

    @ALL : Je prépare un google code sur ce projet qui arrive très bientôt, avec quelque features sympa, notamment l’autogénération de classe actionscript suivant vos XSD. Stay tuned !


Soit pas timide...

Powered by WP Hashcash


Bad Behavior has blocked 333 access attempts in the last 7 days.