INaLCO - M2 Traductique
Introduction à XML

Jean-François Perrot 

DOM (Document Object Model) - 2 : Construction

Version en PHP

  1. Principe de la construction en DOM
    1. Construire un objet Document en mémoire plutôt qu'écrire dans un fichier
    2. Rôle prééminent de l'objet DOMDocument

  2. Création d'objets des trois espèces Document, Element et Text
    1. Création (et sortie sur fichier) de l'objet DOMDocument
    2. Création et équipement d'un l'objet Element
    3. Création d'un l'objet DOMText
    4. Récapitulation

  3. Exemple : construire un fichier XML de noms & notes à partir de fichiers-texte
    1. Syntaxe du fichier-texte (source)
    2. Syntaxe (DTD) du fichier XML (but)
    3. Construction de la représentation 1 (par attributs)
    4. Représentation 2 (par enfants)

  4. Variation : modifier un DOMDocument



  1. Principe de la construction en DOM

    1. Construire un objet Document en mémoire plutôt qu'écrire dans un fichier

      Il est toujours possible de créer un fichier XML en écrivant son texte-contenu, caractère par caractère.
      C'est ce que nous avons fait au cours précédent pour produire un fichier HTML à partir d'un fichier contenant un poème.
      Cette manière de faire, simple dans son principe, a le grave inconvénient d'être sujette à l'erreur (en anglais error-prone).

      En effet, l'acte élémentaire de création d'un sommet de l'arbre se réalise comme une succession de trois opérations,
      1. écriture de la balise ouvrante et de ses attributs ;
      2. écriture du contenu - qui peut être compliquée ;
      3. écriture de la balise fermante.
      Il est fort difficile d'assurer que ces trois opérations sont bien exécutées sans faute, dans le bon ordre ...

      La technique DOM offre le moyen de séparer
      • la construction en mémoire d'un objet Document, de manière globale et synthétique
      • et son écriture dans un fichier, de manière séquentielle
        (dans le jargon des informaticiens, cette opération s'appelle sérialisation).
      Même si elle peut sembler pesante, cette façon de faire est clairement préférable, car beaucoup plus facile à vérifier.

    2. Rôle prééminent de l'objet DOMDocument

      Construire un  DOMDocument implique évidemment de construire ses parties : élements, attributs et textes.
      D'une manière analogue à ce qui se passe dans la construction d'un bâtiment préfabriqué, les choses se passent en 2 temps :

      1. Création de l'objet (par exemple un objet DOMElement) -
        qui a lieu, pour ainsi dire, en atelier, hors de son contexte d'utilisation ;

      2. Mise en place de l'objet dans l'arbre en construction,
        en d'autres termes, insertion de l'objet dans son contexte d'utilisation.

      Comme toujours en programmation par objets, chaque phase sera effectuée en envoyant un ordre à un objet.

      • La seconde phase est sans mystère :
        la mise en place d'un objet dans l'arbre est toujours vue comme l'adjonction d'un fils à un père préexistant
        (en somme, comme une naissance dans une famille)
        et jamais comme l'imposition d'un nouveau père à un enfant déjà installé.
        On demande donc à l'objet-père d'accueilir ce nouvel enfant...
        La forme la plus fréquente de cette demande est appendChild(l-enfant), qui respecte l'ordre des dates de naissance.

      • La première en revanche est plus délicate : quel objet va jouer le rôle de l'atelier de fabrication ?
        Ce sera l'objet Document lui-même !
        Plus précisément :
        les objets destinés à prendre place dans l'arbre d'un DOMDocument doivent être créés par ce Document-même.

      D'où il résulte que l'objet DOMDocument doit être créé par d'autres moyens... Voir ci-après.


  2. Création d'objets des trois espèces Document, Element et Text

    illustrée sur l'exemple Voiture.
    1. Création (et sortie sur fichier) de l'objet DOMDocument

      • DOMDocument vide
        En PHP, il suffit de demander la création d'un document vide à la classe DOMDocument elle-même :
        $doc =  new DOMDocument();

        On obtient ainsi un DOMDocument sans feuille de style ni DTD.
        Contrairement à l'arbre XML, ces appendices ne pouront pas être ajoutés par la suite.
        Si on souhaite en faire usage, il faut recourir à un processus de création plus compliqué, en passant par un objet DOMImplementation.

      •  Racine de l'arbre XML
        En PHP, c'est un DOMElement comme les autres : on le crée et on le met en place
        $voiture = $doc->createElement('Voiture');
        $doc->appendChild($voiture);


      • Sortie sur fichier
        En PHP : $doc->save(nom-du-fichier);

        Si on souhaite obtenir la chaîne de caractères (contenu du fichier), demander
        $chn = $doc->saveXML();

    2. Création et équipement d'un l'objet Element

      • Création par $doc->createElement(le-nom);
        $doc est le DOMDocument qui contiendra le DOMElement ainsi créé.

      • Insertion d'attributs par setAttribute(le-nom, la-valeur)
        $voiture->setAttribute('marque', "Citroen");
        $voiture->setAttribute("modele", 'AX');

      • Ajout d'un enfant (à la suite de ses aînés) par appendChild(l-enfant)

      • Mode opératoire : pour éviter les oublis...
        on procèdera systématiquement ainsi :

        1. création :
          $carosse = $doc->createElement('Carosserie');

        2. accrochage immédiat, le DOMElement créé étant encore vide...:
          $voiture
          ->appendChild($carosse);

          N.B. l'objet créateur est $doc, ce n'est pas le père, qui est $voiture...
          DOM ne se conforme pas aux lois de la Nature !

        3. remplissage du nouveau sommet de l'arbre :
          my $capot =  $doc->createElement('Capot');
          $carosse->appendChild($capot);

          etc.

    3. Création d'un l'objet DOMText

      • Création par $doc->createTextNode(le-contenu);
        $doc est le DOMDocument qui contiendra le DOMText ainsi créé.

      • Pratique courante : construction d'un DOMElement-feuille.
        On crée et on met en en place (comme enfant de son père) le DOMElement en question, par exemple notre $capot,
        et on lui attribue comme "premier enfant" un objet DOMText créé "au vol" :
        $capot->appendChild($doc->createTextNode('Impeccable !'));

    4. Récapitulation

      Construction d'une nouvelle Voiture... fichier consVoiture.php

      <?php

      // Construction d'un document XML de type Voiture

      //  Le DOMDocument

      $doc =  new DOMDocument();

      $voiture = $doc->createElement('Voiture');
      $doc->appendChild($voiture);

      // L'arbre XML

      //1. Les attributs de 'Voiture'

      $voiture->setAttribute('marque', "Citroen");
      $voiture->setAttribute("modele", 'AX');

      //2. La carosserie et son capot

      $carosse = $doc->createElement('Carosserie');
      $voiture->appendChild($carosse);

      $capot =  $doc->createElement('Capot');
      $carosse->appendChild($capot);

      $capot->appendChild($doc->createTextNode('Impeccable !'));

      //3. Le moteur et ses entrailles

      $moteur = $doc->createElement('Moteur');
      $voiture->appendChild($moteur);

      $cyls = $doc->createElement('Cylindres');
      $moteur->appendChild($cyls); // et c'est tout : élément vide

      $allum = $doc->createElement('Allumage');
      $moteur->appendChild($allum);

      $allum-> appendChild($doc->createTextNode("En bon etat"));

      //3. La transmission

      $trans = $doc->createElement('Transmission');
      $voiture->appendChild($trans);
      $trans-> setAttribute('type', 'manuel');
      $trans-> setAttribute('nb_vitesses', '4');

      $trans->appendChild($doc->createElement("Boite")); // fils vide

      $trans->appendChild($doc->createElement('TrainAV'));

      $trans->appendChild($doc->createElement('TrainAR'));

      // Et voila tout !

      // Sortie sur fichier
      $doc->save ('Voiture2.xml');
        
      ?>


      Exécution :
      On note que le texte XML contenu dans le fichier ne contient aucune indentation, il est donc illisible pour l'œil humain.
      D'où l'utilité de la commande xmllint --format !

      jfp% php consVoiture.php
      jfp% cat Voiture2.xml
      <?xml version="1.0"?>
      <Voiture marque="Citroen" modele="AX"><Carosserie><Capot>Impeccable !</Capot></Carosserie><Moteur><Cylindres/><Allumage>En bon etat</Allumage></Moteur><Transmission type="manuel" nb_vitesses="4"><Boite/><TrainAV/><TrainAR/></Transmission></Voiture>

      jfp% xmllint --format Voiture2.xml
      <?xml version="1.0"?>
      <Voiture marque="Citroen" modele="AX">
        <Carosserie>
          <Capot>Impeccable !</Capot>
        </Carosserie>
        <Moteur>
          <Cylindres/>
          <Allumage>En bon etat</Allumage>
        </Moteur>
        <Transmission type="manuel" nb_vitesses="4">
          <Boite/>
          <TrainAV/>
          <TrainAR/>
        </Transmission>
      </Voiture>



  3. Exemple : construire un fichier XML de noms & notes à partir de fichiers-texte

    Le problème général pour exploiter des données fournies en texte est de décrire comment ces données sont représentées
    (ce que les informaticiens appellent la syntaxe du fichier).
    L'avantage de XML sur le texte est que cette syntaxe est décrite par la DTD !

    1. Syntaxe du fichier-texte (source)

      • les noms et les notes sont donnés ligne par ligne, un non &  une note par ligne
      • séparateurs entre noms et notes : blancs et tabulations
      • codage UTF-8, sauts de ligne Unix ("\n")

      Exemple, extrait du fichier NomsNotes.txt

      Paulette    09
      Hélène        18
      Julien        13
      Josette        19



    2. Syntaxe (DTD) du fichier XML (but)

      Rappelons les deux structures que nous avons définies précédemment.
      1. Représentation 1 (par attributs)
        Exemple :
        ?xml version="1.0" encoding="UTF-8"?>
        <liste>
         
        <eleve nom="Paulette" note="09"/>
         <eleve nom="Hélène" note="18"/>
         <eleve nom="Julien" note="13"/>
         <eleve nom="
        Josette" note="19"/>
        </liste>


        DTD :
        <?xml version="1.0"?>
        <!-- Tableau Noms-Notes par attributs -->
        <!ELEMENT liste (eleve*)>
        <!ELEMENT eleve EMPTY>
        <!ATTLIST eleve nom CDATA #REQUIRED>
        <!ATTLIST eleve note CDATA #REQUIRED>
      2. Représentation 2 (par enfants)
        Exemple :
        <?xml version="1.0"?>
        <liste>
          <eleve>
            <nom>Paulette</nom>
            <note>09</note>
          </eleve>
          <eleve>
            <nom>Hélène</nom>
            <note>18</note>
          </eleve>
          <eleve>
            <nom>Julien</nom>
            <note>13</note>
          </eleve>
          <eleve>
            <nom>Josette</nom>
            <note>19</note>
          </eleve>
        </liste>



        DTD
        <?xml version="1.0" ?>
        <!-- Tableau Noms-Notes par enfants -->
        <!ELEMENT liste (eleve*)>
        <!ELEMENT eleve (nom, note)>
        <!ELEMENT nom (#PCDATA)>
        <!ELEMENT note (#PCDATA)>

    3. Construction de la représentation 1 (par attributs)

      fichier NN1-FromText.php

      <?php

      function doc1fromText($nomFich){ // construit le DOMDocument
          $doc = new DOMDocument();
          $rac = $doc->createElement("liste");
          $doc->appendChild($rac);

          $lignes = file($nomFich);
          foreach( $lignes as $ligne ){

      // lire le nom et la note
              $nom_note = split("[ \t]+", $ligne);
              $le_nom = $nom_note[0];
              $la_note = rtrim($nom_note[1]);

      // créer et mettre en place le DOMElement "eleve" correspondant
              $elv = $doc->createElement("eleve");
              $rac->appendChild($elv);
              $elv->setAttribute("nom", $le_nom);
              $elv->setAttribute("note", $la_note);
          }
          return $doc;
      }// doc1fromText

      // Exécution
      $doc = doc1fromText("Ex1.txt");
      $doc->save("Ex1.1.xml");

      ?>



    4. Représentation 2 (par enfants)

      fichier NN2-FromText.php
      Le programme est exactement le même, sauf pour la phase de construction de l'élément "eleve".
      Les deux lignes
              $elv->setAttribute("nom", $le_nom);
              $elv->setAttribute("note", $la_note);

      sont remplacées par

              $elt_nom = $doc->createElement("nom");
              $elv->appendChild($elt_nom);
              $elt_nom->appendChild($doc->createTextNode($nom));
             
              $elt_note = $doc->createElement("note");
              $elv->appendChild($elt_note);
              $elt_note->appendChild($doc->createTextNode($note));



  4. Variation : modifier un DOMDocument

    illustrée par l'exemple : ajouter ou modifier une note.

    Cette opération combine la lecture du DOMDocument et la création d'éléments que nous venons de voir.
    En voici une réalisation pour la représentation 1 (par attributs)
    fichier
    DonnNote.php

    <?php

    function donnerNote($doc, $nom_donne, $note_donnee){

        $leseleves = $doc->getElementsByTagName('eleve');
       
        foreach( $leseleves as $leleve ){
            if( $leleve->getAttribute('nom') == $nom_donne ){
                $leleve->setAttribute('note', $note_donnee);
                return $doc;
            }
        }
        // et si on n'a pas trouvé le nom, on ajoute un élève
        $elv = $doc->createElement('eleve');
        $doc->lastChild->appendChild($elv);
        $elv->setAttribute('nom', $nom_donne);
        $elv->setAttribute('note', $note_donnee);
        return $doc;
    }//donnerNote

    $doc = new DOMDocument();
    $doc->load('Ex1.xml');
    donnerNote($doc, 'Pierre', 14);
    donnerNote($doc, 'Jules', 14);
    $doc->save('Ex1b.xml');

    ?>