EPITA
XML et les services Web

Jean-François Perrot 

SAX en Java

  1. Références

  2. Principe : un parseur à événements (event-based parser)
    1. Idée
    2. En somme ...
    3. Exemple 1 : principe d'un parseur-indenteur

  3. L'API SAX en Java : Mode d'emploi élémentaire
    1. L'interface ContentHandler 
    2. Mise en œuvre
    3. Exemple : réalisation complète du parseur-indenteur

  4. Traitement d'erreurs
    1. SAXException
    2. org.xml.sax.ErrorHandler
    3. Validation
      1. Activation
      2. Validateur générique
      3. Illustrations
      4. Le code

  5. Un exemple illustratif

Références


Bonnne introduction :
http://www.ibm.com/developerworks/xml/library/x-saxapi/

Une autre, assez complémentaire : le chapitre 6 du livre Processing XML with Java d'Elliote Rusty Harold
http://www.cafeconleche.org/books/xmljava/

JavaDoc du package org.xml.sax
http://java.sun.com/j2se/1.5.0/docs/api/org/xml/sax/package-summary.html

JavaDoc du package  org.xml.sax.helpers
http://www.cs.ubc.ca/local/computing/software/j2sdk-1.4.2/docs/api/org/xml/sax/helpers/package-summary.html
(introuvable sur java.sun.com !)

Principe : un parseur à événements (event-based parser)

  1. Idée

    Le parseur parcourt le fichier caractère par caractère.
    Il sait reconnaître les balises, les attributs, les contenus textuels
    en d'autres termes, il sait la grammaire XML.

    Il est donc capable de dire je viens de lire une balise ouvrante dont voici
    ce qu'il exprime en appelant une procédure qui va recevoir ces deux arguments.
    En jargon technique on dit que le parseur "déclenche un événement", et la procédure appelée est qualifiée de callback.

    Même jeu pour les balises fermantes (avec le nom seulement)
    et pour les contenus textuels (avec la chaîne de caractères trouvée)
  2. En somme ...

    on décharge le programmeur de la tâche de reconnaître les mots du texte.
    Chaque fois que le parseur a trouvé quelque chose d'intéressant, il le signale par un "événement".
    Le programmeur doit traiter ces événements pour obtenir le résultat de son choix.
    Le traitement en question dépend en général (comme le résultat cherché) de la DTD du fichier...
    il est réalisé par une séquence d'actions.

    Attention ! Les callbacks sont des procédures et non des fonctions :
    elles opèrent un effet de bord (chagement de l'état du système) plutôt que de renvoyer un résultat construit.
    Cette séquence d'actions peut construire un objet-résultat...

    La programmation des parseurs SAX fait appel à des mécanismes "de bas niveau" :
    les actions qui constituent la séquence d'actions sont très élémentaires,
    et leur caractère séquentiel (ou analytique) est très prégnant (fort peu synthétique).
  3. Exemple 1 : principe d'un parseur-indenteur


L'API SAX en Java : Mode d'emploi élémentaire

  1. L'interface ContentHandler 

    Les procédures callback en question sont spécifiées dans l'interface org.xml.sax.ContentHandler.
    Il y en a 11 au total. Les trois principales sont

    1. void startElement(String uri, String name, String qualif, Attributes attrs)
      appelée à la lecture d'une balise ouvrante de nom name,
      dont la table (nom-valeur) des attributs est représentée par l'argument attrs, de type org.xml.sax.Attributes
      (nous parlerons de uri et de qualif plus tard).

    2. void endElement(String uri, String name, String qualif)
      appelée à la lecture d'une balise fermante de nom name.

    3. void characters(char[] ch, int start, int length)
      appelée à la lecture d'un contenu textuel,
      qui se trouve logé dans le tableau ch entre les indices start et start+length.

    Exemple 1 (suite) : les callbacks du parseur-indenteur

        public void startElement(String uri, String name, String qualif, Attributes at) {
            indent();
            System.out.print("<"+name);
            for( int i = 0; i < at.getLength(); i++ ){
                System.out.print(" "+ at.getLocalName(i)+":"+at.getValue(i));
            }
            System.out.println(">");
            prof++;
        }

        public void endElement(String uri, String name, String qualif){
            prof--;       
            indent();
            System.out.println("</"+name+">");

        }

        public void characters(char[] ch, int start, int length) {
            indent();
            System.out.print(Indenteur.retrait); // un de plus
            for( int i = 0; i < length; i++ ){
                System.out.print(ch[start+i]);
            }
            System.out.println();
        }



  2. Mise en œuvre

    1. Se procurer une implémentation de l'interface org.xml.sax.XMLReader
      par exemple : org.apache.xerces.parsers.SAXParser
      ou bien (ce qui sera employé ci-après) l'implémentation fournie en standard à partir de Java 1.4
      à partir de la classe org.xml.sax.helpers.XMLReaderFactory.

    2. Construire une classe implémentant l'interface org.xml.sax.ContentHandler.
      en pratique on écrira une sous-classe de org.xml.sax.helpers.DefaultHandler
      (ce qui évitera d'avoir à implémenter les 11 méthodes de l'interface !)
      dans laquelle on redéfinira les trois méthodes startElement, endElement et characters.
      appelons-la MonHandler.

    3. Après quoi :

      1. On crée un objet parseur :
        XMLReader parseur = XMLReaderFactory.createXMLReader();


      2. On crée un objet handler :
        MonHandler handler = new MonHandler(); 

      3. On équipe le parseur avec le handler, dans le double rôle de
        • gestionnaire de contenu (content handler) : parseur.setContentHandler(handler);
        • gestionnaire d'erreurs (error handler) : parseur.setErrorHandler(handler);

      4. et enfin on lance le processus : parseur.parse(nom_fichier);
  3. Exemple : réalisation complète du parseur-indenteur

    fichier Indenteur.java
    /* L'indenteur de base, avec les attributs et les contenus */

    import org.xml.sax.XMLReader;
    import org.xml.sax.Attributes;
    import org.xml.sax.SAXException;
    import org.xml.sax.SAXParseException;
    import org.xml.sax.helpers.XMLReaderFactory;
    import org.xml.sax.helpers.DefaultHandler;

    public class Indenteur extends DefaultHandler {

        private static String retrait = "  "; // 2 espaces

        private int prof = 0; // globale, gérée avec "effet de bord"
       
        private void indent() {
            for( int i = 0; i < prof; i++ ) {
                System.out.print(Indenteur.retrait);
            }
        }

        public Indenteur() {
            super();
        }// constructeur

        public void startElement(String uri, String name, String qualif, Attributes at) {
            voir ci-dessus
        }

        public void endElement(String uri, String name, String qualif){
            voir ci-dessus
        }

        public void characters(char[] ch, int start, int length) {
             voir ci-dessus
        }

    //------------------------------------------------------------------------------------

        public static void main(String[] args) throws Exception {
            if( args.length != 1 ){
                System.out.println("Il faut dire : java Indenteur un-nom-de-fichier-XML !");
            } else {
                String uri = args[0];
                System.out.println("Analyse du fichier XML : "+uri);
       
                XMLReader parseur = XMLReaderFactory.createXMLReader();

                Indenteur handler = new Indenteur();
                parseur.setContentHandler(handler);
                parseur.setErrorHandler(handler);
           
                try {
                    parseur.parse(uri);
                } catch (SAXException e) {
                    System.out.println(e.getMessage());
                }
                System.out.println("Termine' !");
            }//else
        }//main
    }//Indenteur


Traitement d'erreurs

Dans tout processus de lecture d'un fichier, il est essentiel de prendre des précautions à l'égard des vices de forme qui peuvent affecter ce fichier.
Le traitement des erreurs de syntaxe fait donc partie intégrante du cahier des charges de tout parseur.
À cette fin, SAX offre un système très élaboré.

Un exemple illustratif

Pour donner une idée du style de programmation que SAX conduit à adopter, voici un exemple ultra-simple :
dans un fichier de noms & notes, trouver la note d'un élève connu par son nom.