Les bases de XML dans Squeak

From OFSET Wiki

Jump to: navigation, search

Contents

[edit] Contenu

Dans cette documentation, nous allons apprendre les bases pour la manipulation des fichiers XML sous Squeak avec les points suivants :

  • lire un fichier XML
  • parcourir un document XML
  • sauvegarder un document XML dans un fichier
  • construire un document XML

[edit] Pré-requis

Il vous faut connaître les bases de XML. Pour cela les références ne manquent pas sur le web. Vous pouvez par exemple vous former à partir du site w3school.

[edit] Les classes en jeux

Les principales classes utilisées sont :

  • XMLDOMParser : qui permet d'analyser le fichier et d'obtenir un document "structuré" (des objets);
  • XMLElement et XMLDocument : représentent un noeud de l'arbre XML et l'abstraction du document complet;

[edit] Organisation

Ces classes s'organisent de la façon suivante :

Image:XMLDansSqueak-classes-de-base.png

La classe XMLNode est l'abstraction dont héritent toutes les classes qui interviennent dans la constitution d'un document XML. XMLNodeWithElements est un noeud qui peut contenir des XMLElement.

La classe XMLElement est la classe concrète la plus utilisée pour la représentation d'un document, c'est elle qui représente un noeud avec les variables d'instance suivantes :

  • name : le nom du noeud, c'est la balise XML;
  • contents : c'est une collection de XMLStringNode; chaque XMLStringNode contient une chaine de caractères; contents correspond au texte d'un niveau à l'exclusion des sous-noeuds;
  • atributes : c'est un dictionaire qui stocke les couples (nom, valeur) des attributs d'un noeud.

La classe XMLDocument représente le point d'entrée pour un document XML, ses éléments sont les noeuds du premier niveau de l'arbre.

[edit] Les différents contenus d'un noeud

Un noeud (un XMLElement) contient potentiellement trois types de contenu :

  • des noeuds fils
  • des attributs
  • du texte

[edit] Les noeuds fils

Ils sont stockés dans la variable d'instance XMLNodeWithElements>>elements, une OrderedCollection de XMLElements. La structure arborescente régulière repose sur cette collection.

[edit] Les attributs

Chaque noeud peut comporter un certains nombre d'attributs nommés associés à une valeur. Ces attributs sont stockés au niveau de chaque noeud dans la variable d'instance XMLElement>>attributes, un Dictionary. La clé d'accès et la valeur sont des String.

[edit] Le texte

Un noeud peut comporter plusieurs chaines de caractères qui représentent sont contenu. Le contenu est stocké dans la variable d'instance XMLElement>>contents, une OrderedCollection de XMLStringNode. Habituellement, seuls les noeuds feuilles comportent du contenu, dans ce cas, XMLElement>>contents ne comprend qu'un seul XMLStringNode. Un noeud comportant des noeuds fils peut aussi avoir du contenu. Dans l'exemple suivant, le noeud #realisateur a trois chaînes de caractères pour le contenus (pas avant 12 ans,suspens assez terrifiant et guerre vietnam) , chacune insérées avant le noeud fils dont elle fait office de commentaire :

<realisateur nom="Stanley Kubrick">
   pas avant 12 ans
   <film>Eyes Wide Shut</film>
   suspens assez terrifiant
   <film>The shining</film>
   guerre vietnam
   <film>Full Metal Jacket</film>
</realisateur>

Dans ce cas, pour le noeud #realisateur, la variable d'instance XMLElement>>contents contient trois XMLStringNode; les noeuds feuilles #film comportent eux un seul XMLStringNode.

Il s'agit bien évidemment d'un exemple à ne pas suivre !

En effet, aucune information de position n'est associée à un contenu. Le fait que pas avant 12 ans est relatif à <film>Eyes Wide Shut</film> est complètement implicite. Par exemple, le réenvoi sur le Transcript illustre cela :

<realisateur nom="Stanley Kubrick">pas avant 12 ans
        suspens assez terrifiant
        guerre vietnam
<film>Eyes Wide Shut</film>
<film>The shining</film>
<film>Full Metal Jacket</film></realisateur>

On voit que les XMLStringNode contenus sont placés tous avant les éléments fils, on ne retrouve pas la forme lue.

[edit] Lecture d'un document XML

[edit] Le document exemple

Dans la suite, on va utiliser le document suivant pour les exemples :

<?xml version="1.0"?>
<filmographie>
    <realisateur nom="Stanley Kubrick">
        <film>Eyes Wide Shut</film>
        <film>The shining</film>
        <film>Full Metal Jacket</film>
    </realisateur>
    <realisateur nom="Steven Spielberg">
        <film>E.T</film>
        <film>Munich</film>
        <film>War of the Worlds</film>
    </realisateur>
</filmographie>

[edit] A partir d'un fichier

Pour analyser un fichier XML et disposer de tous son contenu dans l'image, on peut utiliser la classe XMLDOMParser. Par exemple, la méthode XMLDOMParser class>>#parseDocumentFromFileNamed: permet de lire un fichier. Voici comment l'utiliser pour lire le fichier xml exemple films.xml :

| document |
document := XMLDOMParser parseDocumentFromFileNamed: 'films.xml'.
Transcript show: document. " pour vérifier "

On obtient ainsi une instance de la classe XMLDocument qui représente le point d'entrée de l'arbre XML.

[edit] A partir d'un Stream

On peut analyser un stream avec SAXHandler class>>#parseDocumentFrom:. Le stream analysé est passé en argument, le document contruit est retourné si on utilise un XMLDOMParser. Par exemple :

XMLDOMParser parseDocumentFrom: (ReadStream on: '<trux> yoyo </trux>')

Voici un autre exemple avec un dialogue de sélection de fichier, le dialogue retourne un stream qui peut ainsi directement être mangé par l'analyseur (on n'oublie pas de fermer le stream à la fin...) :

| fstr xmldoc | 
xmldoc := XMLDOMParser parseDocumentFrom:  (fstr := FileList2 modalFileSelectorForSuffixes: #('xml') ).
fstr close.
xmldoc explore

[edit] Lecture de fichiers avec espaces de nommage

Tout simplement avec XMLDOMParser class>>parseDocumentFrom:useNamespaces:

| fstr xmldoc | 
xmldoc := XMLDOMParser parseDocumentFrom:  
    (fstr := FileList2 modalFileSelectorForSuffixes: #('xml') ) 
    useNamespaces: true.
fstr close.
xmldoc explore

[edit] Parcourir un document XML

Ce chapitre concerne les différents parcours possibles par itération, en profondeur ou non.

[edit] Le document exemple

Dans la suite, on va utiliser le document suivant pour les exemples :

<?xml version="1.0"?>
<filmographie>
    <realisateur nom="Stanley Kubrick">
        <film>Eyes Wide Shut</film>
        <film>The shining</film>
        <film>Full Metal Jacket</film>
    </realisateur>
    <realisateur nom="Steven Spielberg">
        <film>E.T</film>
        <film>Munich</film>
        <film>War of the Worlds</film>
    </realisateur>
</filmographie>

[edit] Parcours des éléments d'un noeud

Les méthodes qui nous intéressent ici se trouvent dans les catégories accessing et searching de la classe XMLNode. Ce chapitre présente certaines de ces méthodes.

[edit] Pour tous les noeuds fils

Pour parcourir les éléments d'un noeud (à partir d'un XMLNodeWithElements), on peut envoyer le message #elementsDo: au noeud à parcourir. L'argument est un block à un argument. Pendant l'itération, l'argument du block référence le noeud courant. Voici un exemple de parcours, qui envoie au Transcript la balise des éléments contenus dans un document :

| docXml |
docXml := XMLDOMParser parseDocumentFromFileNamed: 'films.xml'.
docXml elementsDo: [:n | Transcript show: n tag, String cr]

Le document exemple n'ayant qu'un seul élément, le résultat suivant est affiché dans le Transcript :

filmographie

[edit] Pour certains noeuds fils

Si on veut exécuter une action pour les noeuds fils correspondant à une balise particulière, on peut utiliser #tagsNamed:childrenDo: :

| docXml |
docXml := XMLDOMParser parseDocumentFromFileNamed: 'films.xml'.
docXml tagsNamed: #filmographie childrenDo: [:e | Transcript show: (e tag, ' ')]

Ce qui donne :

filmographie

En effet, le document se constitue d'un seul noeud fils avec justement la balise #filmographie


[edit] Pour certains noeuds en profondeur

Si on veut atteindre des noeuds plus profonds que les fils et exécuter une action, alors, on peut utiliser #tagsNamed:do:. Par exemple, avec notre fichier de test, pour atteindre depuis le document racine, les noeuds #realisateur et les envoyer sur le Transcript :

| docXml |
docXml := XMLDOMParser parseDocumentFromFileNamed: 'films.xml'.
docXml tagsNamed: #realisateur do: [:e | Transcript show:  '[',e tag,']']

le résultat suivant est affiché dans le Transcript :

[realisateur][realisateur]

[edit] Chercher un noeud particulier

[edit] Recherche simple

On peut rechercher un noeud correspond à une balise particulière avec XMLNode>>#firstTagNamed:. L'envoi de ce message à un noeud retourne le premier noeud accessible en profondeur dans l'arbre dont le noeud receveur est racine et correspondant à la balise passée en argument. Par exemple :

| nd |
nd := (XMLDOMParser parseDocumentFromFileNamed: 'films.xml' ) firstTagNamed: #film.
Transcript show: nd

retourne le premier noeud du document correspondant à la balise #film. Voici le résultat sur le Transcript :

<film>Eyes Wide Shut</film>

[edit] Recherche paramétrée

La recherche peut être en plus paramétrée par un block avec XMLNode>>#firstTagNamed:with:. L'envoi de ce message à un noeud retourne le premier noeud accessible en profondeur dans l'arbre dont le noeud receveur est racine, correspondant à la balise passée en premier argument et pour lequel l'évaluation du bloc passé en second argument retourne true. Il s'agit d'un block à un argument qui est la référence du noeud testé pendant l'exécution du block. Par exemple :

| nd |
nd := (XMLDOMParser parseDocumentFromFileNamed: 'films.xml' ) 
       firstTagNamed: #film 
       with: [:n | n contents size = 1 and: [n contents first string = 'Munich']].
Transcript show: nd

retourne le premier noeud du document correspondant à la balise #film qui en plus contient uniquement et exactement le texte Munich. Voici le résultat sur le Transcript :

<film>Munich</film>

[edit] Sauvegarder un document XML dans un fichier

Pour écrire un document XML dans un flot, on lui envoi le message #printOn: avec un Stream en argument. Voici un exemple qui lit un document xml depuis un fichier et le sauvegarde immédiatement dans un autre fichier :

| xmldoc stream |
xmldoc := (XMLDOMParser parseDocumentFromFileNamed: 'films.xml' ).
stream := FileStream fileNamed: 'films2.xml'.
xmldoc printOn: stream.
stream close.

[edit] Créer son propre document XML

[edit] Principe

Pour créer un document structuré par programme, on peut utiliser un XMLDOMParser. La création du document s'effectue comme une série d'empilements et de dépilements des noeuds de l'arbre Xml.

On dispose des méthodes suivantes :

  • XMLDOMParser>>startDocument : pour placer le receveur en état création de document;
  • XMLDOMParser>>endDocument : pour terminer la création du document;
  • XMLDOMParser>>startElement:attributeList: : pour empiler un élément avec éventuellement des attributs;
  • XMLDOMParser>>endElement: : pour dépiler l'élément courant;
  • XMLElement>>addContent: : pour ajouter un contenu (XMLStringNode) à un noeud;

[edit] Un document simple

Voici donc le code complet pour recréer le document films.xml. L'indentation met en évidence la gestion de la pile d'éléments :

   | parser  nom elt stream |
   parser := XMLDOMParser new.
   parser startDocument.

   parser startElement: 'Filmographie' attributeList: Dictionary new.
      nom := Dictionary new.
      nom at: 'nom' put: 'Stanley Kubrick'.
      parser startElement: 'realisateur' attributeList: nom.
         parser startElement: 'film' attributeList: Dictionary new.
            elt := XMLStringNode string: 'Eyes Wide Shut'.
            parser top addContent: elt.
         parser endElement: 'film'.
         parser startElement: 'film' attributeList: Dictionary new.
            elt := XMLStringNode string: 'Full Metal Jacket'.
            parser top addContent: elt.
         parser endElement: 'film'.
         parser startElement: 'film' attributeList: Dictionary new.
            elt := XMLStringNode string: 'The Shining'.
            parser top addContent: elt.
         parser endElement: 'film'.
      parser endElement: 'realisateur'.

      nom := Dictionary new.
      nom at: 'nom' put: 'Steven Spielberg'.
      parser startElement: 'realisateur' attributeList: nom.
         parser startElement: 'film' attributeList: Dictionary new.
            elt := XMLStringNode string: 'E.T'.
            parser top addContent: elt.
         parser endElement: 'film'.
         parser startElement: 'film' attributeList: Dictionary new.
            elt := XMLStringNode string: 'Munich'.
            parser top addContent: elt.
         parser endElement: 'film'.
         parser startElement: 'film' attributeList: Dictionary new.
            elt := XMLStringNode string: 'War of the Worlds'.
            parser top addContent: elt.
         parser endElement: 'film'.
      parser endElement: 'realisateur'.
   parser endElement: 'Filmographie'.

   parser endDocument.
   stream := FileStream fileNamed: 'films.xml'.
   parser document printOn: stream.
   stream close.
Personal tools