IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Développons en Java v 2.20   Copyright (C) 1999-2021 Jean-Michel DOUDOUX.   
[ Précédent ] [ Sommaire ] [ Suivant ] [ Télécharger ]      [ Accueil ] [ Commentez ]


 

43. SAX (Simple API for XML)

 

chapitre 4 3

 

Niveau : niveau 3 Intermédiaire 

 

SAX est l'acronyme de Simple API for XML. Cette API a été développée par David Megginson.

Ce type de parseur utilise des événements pour piloter le traitement d'un fichier XML. Un objet (nommé handler en anglais) doit implémenter des méthodes particulières définies dans une interface de l'API pour fournir les traitements à réaliser : selon les événements, le parseur appelle ces méthodes.

Les dernières informations concernant cette API sont disponible à l'URL : www.megginson.com/SAX/index.phpl

Les classes de l'API SAX sont regroupées dans le package org.xml.sax

Ce chapitre contient plusieurs sections :

 

 

43.1. L'utilisation de SAX de type 1

SAX type 1 est composé de deux packages :

  • org.xml.sax :
  • org.xml.sax.helpers :

SAX définit plusieurs classes et interfaces :

  • les interfaces implémentées par le parseur : Parser, AttributeList et Locator
  • les interfaces implémentées par le handler : DocumentHandler, ErrorHandler, DTDHandler et EntityHandler
  • les classes de SAX :
  • des utilitaires rassemblés dans le package org.xml.sax.helpers notamment la classe ParserFactory

Les exemples de cette section utilisent la version 2.0.15 du parseur xml4j d'IBM.

Pour parser un document XML avec un parseur XML SAX de type 1, il faut suivre les étapes suivantes :

  • créer une classe qui implémente l'interface DocumentHandler ou hérite de la classe org.xml.sax.HandlerBase et qui se charge de répondre aux différents événements émis par le parseur
  • créer une instance du parseur en utilisant la méthode makeParser() de la classe ParserFactory.
  • associer le handler au parseur grâce à la méthode setDocumentHandler()
  • exécuter la méthode parse() du parseur
Exemple : avec XML4J
import org.xml.sax.*;
import org.xml.sax.helpers.ParserFactory;
import com.ibm.xml.parsers.*;
import java.io.*;

public class MessageXML {
  static final String DONNEES_XML =
    "<?xml version=\"1.0\"?>\n"
    +"<BIBLIOTHEQUE>\n"
    +"  <LIVRE>\n"
    +"    <TITRE>titre livre 1</TITRE>\n"
    +"    <AUTEUR>auteur 1</AUTEUR>\n"
    +"    <EDITEUR>editeur 1</EDITEUR>\n"
    +"  </LIVRE>\n"
    +"  <LIVRE>\n"
    +"    <TITRE>titre livre 2</TITRE>\n"
    +"    <AUTEUR>auteur 2</AUTEUR>\n"
    +"    <EDITEUR>editeur 2</EDITEUR>\n"
    +"  </LIVRE>\n"
    +"  <LIVRE>\n"
    +"    <TITRE>titre livre 3</TITRE>\n"
    +"    <AUTEUR>auteur 3</AUTEUR>\n"
    +"    <EDITEUR>editeur 3</EDITEUR>\n"
    +"  </LIVRE>\n"
    +"</BIBLIOTHEQUE>\n";

  static final String CLASSE_PARSER = "com.ibm.xml.parsers.SAXParser";

  /**
   * Lance l'application.
   * @param args un tableau d'arguments de la ligne de commandes
   */
  public static void main(java.lang.String[] args) {
  
    MessageXML m = new MessageXML();
    m.parse();

    System.exit(0);
  }

  public MessageXML() {
    super();
  }

  public void parse() {
    TestXMLHandler handler = new TestXMLHandler();
  
    System.out.println("Lancement du parseur");

    try {
      Parser parser = ParserFactory.makeParser(CLASSE_PARSER);

      parser.setDocumentHandler(handler);
      parser.setErrorHandler((ErrorHandler) handler);

      parser.parse(new InputSource(new StringReader(DONNEES_XML)));

    } catch (Exception e) {
      System.out.println("Exception capturée : ");
      e.printStackTrace(System.out);
      return;
    }
  }
}

 

Il faut ensuite créer la classe du handler.

Exemple :
import java.util.*;

/**
 * Classe utilisée pour gérer les événements émis par SAX lors du traitement du fichier XML
 */
public class TestXMLHandler extends org.xml.sax.HandlerBase {
  public TestXMLHandler() {
    super();
  }
  
  /**
   * Actions à réaliser sur les données
   */
  public void characters(char[] caracteres, int debut, int longueur) {
    String donnees = new String(caracteres, debut, longueur);
    System.out.println("   valeur = *" + donnees + "*");
  }
  
  /**
   * Actions à réaliser lors de la fin du document XML.
   */
  public void endDocument() {
    System.out.println("Fin du document");
  }
  
  /**
   * Actions à réaliser lors de la détection de la fin d'un élément.
   */
  public void endElement(String name) {
    System.out.println("Fin tag " + name);
  }
  
  /**
   * Actions à réaliser au début du document.
   */
  public void startDocument() {
    System.out.println("Debut du document");
  }
  
  /**
   * Actions à réaliser lors de la détection d'un nouvel élément.
   */
  public void startElement(String name, org.xml.sax.AttributeList atts) {
    System.out.println("debut tag : " + name);
  }
}

Résultat :
Lancement du parser
Debut du document
debut tag : BIBLIOTHEQUE
   valeur = *
  *
debut tag : LIVRE
   valeur = *
    *
debut tag : TITRE
   valeur = *titre livre 1*
Fin tag TITRE
   valeur = *
    *
debut tag : AUTEUR
   valeur = *auteur 1*
Fin tag AUTEUR
   valeur = *
    *
debut tag : EDITEUR
   valeur = *editeur 1*
Fin tag EDITEUR
   valeur = *
  *
Fin tag LIVRE
   valeur = *
  *
debut tag : LIVRE
   valeur = *
    *
debut tag : TITRE
   valeur = *titre livre 2*
Fin tag TITRE
   valeur = *
    *
debut tag : AUTEUR
   valeur = *auteur 2*
Fin tag AUTEUR
   valeur = *
    *
debut tag : EDITEUR
   valeur = *editeur 2*
Fin tag EDITEUR
   valeur = *
  *
Fin tag LIVRE
   valeur = *
  *
debut tag : LIVRE
   valeur = *
    *
debut tag : TITRE
   valeur = *titre livre 3*
Fin tag TITRE
   valeur = *
    *
debut tag : AUTEUR
   valeur = *auteur 3*
Fin tag AUTEUR
   valeur = *
    *
debut tag : EDITEUR
   valeur = *editeur 3*
Fin tag EDITEUR
   valeur = *
  *
Fin tag LIVRE
   valeur = *
*
Fin tag BIBLIOTHEQUE
Fin du document

Un parseur SAX peut créer plusieurs types d'événements. Les principales méthodes pour y répondre sont :

Evénement

Rôle

startElement()

cette méthode est appelée lors de la détection d'un tag de début

endElement()

cette méthode est appelée lors de la détection d'un tag de fin

characters()

cette méthode est appelée lors de la détection de données entre deux tags

startDocument()

cette méthode est appelée lors du début du traitement du document XML

endDocument()

cette méthode est appelée lors de la fin du traitement du document XML


La classe handler doit redéfinir certaines de ces méthodes selon les besoins des traitements.

En règle générale :

  • il faut sauvegarder dans une variable le tag courant détecté dans la méthode startElement()
  • traiter les données en fonction du tag courant dans la méthode characters()

La sauvegarde du tag courant est obligatoire car la méthode characters() ne contient pas dans ses paramètres le nom du tag correspondant aux données.

Si les données contenues dans le document XML contiennent plusieurs occurrences qu'il faut gérer avec une collection qui contiendra des objets encapsulant les données, il faut :

  • gérer la création d'un objet dans la méthode startElement() lors de la rencontre du tag de début d'un nouvel élément de la liste
  • alimenter les attributs de l'objet avec les données de chaque tag utile dans la méthode characters()
  • gérer l'ajout de l'objet à la collection dans la méthode endElement() lors de la rencontre du tag de fin d'élément de la liste

La méthode characters() est appelée lors de la détection de données entre un tag de début et un tag de fin mais aussi entre un tag de fin et le tag de début suivant lorsqu'il y a des caractères entre les deux. Ces caractères ne sont pas des données mais des espaces, des tabulations, des retour chariots et certains caractères non visibles.

Pour éviter de traiter les données de ces événements, il y a plusieurs solutions :

  • supprimer tous les caractères entre les tags : tous les tags et les données sont rassemblés sur une seule et unique ligne. L'inconvénient de cette méthode est que le message est difficilement lisible par un être humain.
  • une autre méthode consiste à remettre à vide la chaîne de caractères qui contient le tag courant (alimentée dans la méthode startElement()) dans la méthode endElement(). Il suffit alors d'effectuer les traitements dans la méthode characters() uniquement si le tag courant est différent de vide

Exemple :
import java.util.*;

/**
 * Classe utilisée pour gérer les événement émis par SAX lors du traitement du fichier XML
 */
public class TestXMLHandler extends org.xml.sax.HandlerBase {
  private String tagCourant = "";
  public TestXMLHandler() {
    super();
  }
  
  /**
   * Actions à réaliser sur les données
   */
  public void characters(char[] caracteres, int debut, int longueur) {
    String donnees = new String(caracteres, debut, longueur);
    if (!tagCourant.equals("") {
      System.out.println("  Element " + tagCourant 
        + ", valeur = *" + donnees + "*");
    }
  }
  
  /**
   * Actions à réaliser lors de la fin du document XML.
   */
  public void endDocument() {
    System.out.println("Fin du document");
  }
  
  /**
   * Actions à réaliser lors de la détection de la fin d'un élément.
   */
  public void endElement(String name) {
    tagCourant = "";
    System.out.println("Fin tag " + name);
  }
  
  /**
   * Actions à réaliser au début du document.
   */
  public void startDocument() {
    System.out.println("Debut du document");
  }
  
  /**
   * Actions a réaliser lors de la détection d'un nouvel élément.
   */
  public void startElement(String name, org.xml.sax.AttributeList atts) {
    tagCourant = name;
    System.out.println("debut tag : " + name);
  }
}

Résultat :
Lancement du parser
Debut du document
debut tag : BIBLIOTHEQUE
   Element BIBLIOTHEQUE, valeur = *
  *
debut tag : LIVRE
   Element LIVRE, valeur = *
    *
debut tag : TITRE
   Element TITRE, valeur = *titre livre 1*
Fin tag TITRE
debut tag : AUTEUR
   Element AUTEUR, valeur = *auteur 1*
Fin tag AUTEUR
debut tag : EDITEUR
   Element EDITEUR, valeur = *editeur 1*
Fin tag EDITEUR
Fin tag LIVRE
debut tag : LIVRE
   Element LIVRE, valeur = *
    *
debut tag : TITRE
   Element TITRE, valeur = *titre livre 2*
Fin tag TITRE
debut tag : AUTEUR
   Element AUTEUR, valeur = *auteur 2*
Fin tag AUTEUR
debut tag : EDITEUR
   Element EDITEUR, valeur = *editeur 2*
Fin tag EDITEUR
Fin tag LIVRE
debut tag : LIVRE
   Element LIVRE, valeur = *
    *
debut tag : TITRE
   Element TITRE, valeur = *titre livre 3*
Fin tag TITRE
debut tag : AUTEUR
   Element AUTEUR, valeur = *auteur 3*
Fin tag AUTEUR
debut tag : EDITEUR
   Element EDITEUR, valeur = *editeur 3*
Fin tag EDITEUR
Fin tag LIVRE
Fin tag BIBLIOTHEQUE
Fin du document

  • enfin il est possible de vérifier si le premier caractère des données contenues en paramètre de la méthode characters() est un caractère de contrôle ou non grâce à la méthode statique isISOControl() de la classe Character

Exemple :
...
/**
 * Actions à réaliser sur les données
 */
public void characters(char[] caracteres, int debut, int longueur) {
	String donnees = new String(caracteres, debut, longueur);

	if (!tagCourant.equals("")) {	
		if(!Character.isISOControl(caracteres[debut])) {
    			System.out.println("   Element " + tagCourant 
                 +", valeur = *" + donnees + "*");
		}
	}
}		
...

Résultat :
Lancement du parser
Debut du document
debut tag : BIBLIOTHEQUE
debut tag : LIVRE
debut tag : TITRE
   Element TITRE, valeur = *titre livre 1*
Fin tag TITRE
debut tag : AUTEUR
   Element AUTEUR, valeur = *auteur 1*
Fin tag AUTEUR
debut tag : EDITEUR
   Element EDITEUR, valeur = *editeur 1*
Fin tag EDITEUR
Fin tag LIVRE
debut tag : LIVRE
debut tag : TITRE
   Element TITRE, valeur = *titre livre 2*
Fin tag TITRE
debut tag : AUTEUR
   Element AUTEUR, valeur = *auteur 2*
Fin tag AUTEUR
debut tag : EDITEUR
   Element EDITEUR, valeur = *editeur 2*
Fin tag EDITEUR
Fin tag LIVRE
debut tag : LIVRE
debut tag : TITRE
   Element TITRE, valeur = *titre livre 3*
Fin tag TITRE
debut tag : AUTEUR
   Element AUTEUR, valeur = *auteur 3*
Fin tag AUTEUR
debut tag : EDITEUR
   Element EDITEUR, valeur = *editeur 3*
Fin tag EDITEUR
Fin tag LIVRE
Fin tag BIBLIOTHEQUE
Fin du document

SAX définit une exception de type SAXParserException lorsque le parseur détecte une erreur dans le document en cours de traitement. Les méthodes getLineNumber() et getColumnNumber() permettent d'obtenir la ligne et la colonne où l'erreur a été détectée.

Exemple :
try {
...
}  catch (SAXParseException e) { 
  System.out.println("Erreur lors du traitement du document XML");
  System.out.println(e.getMessage());
  System.out.println("ligne : "+e.getLineNumber());
  System.out.println("colonne : "+e.getColumnNumber());
  }

Pour les autres erreurs, SAX définit l'exception SAXException.

 

43.2. L'utilisation de SAX de type 2

SAX de type 2 apporte principalement le support des espaces de noms. Les classes et les interfaces sont toujours définies dans les packages org.xml.sax et ses sous-packages.

SAX de type 2 définit quatre interfaces que l'objet handler doit ou peut implémenter :

  • ContentHandler : interface qui définit les méthodes appelées lors du traitement du document
  • ErrorHandler : interface qui définit les méthodes appelées lors du traitement des warnings et des erreurs
  • DTDHandler : interface qui définit les méthodes appelées lors du traitement de la DTD
  • EntityResolver

Plusieurs classes et interfaces de SAX de type 1 sont deprecated :

 
ancienne entité SAX 1
nouvelle entité SAX 2
Interface
org.xml.sax.Parser
XMLReader
org.xml.sax.DocumentHandler
ContentHandler
org.xml.sax.AttributeList
Attributes
Classes
org.xml.sax.helpers.ParserFactory
 
org.xml.sax.HandlerBase
DefaultHandler
org.xml.sax.helpers.AttributeListImpl
AttributesImpl

Les principes de fonctionnement de SAX 2 sont très proches de SAX 1.

Exemple :
import org.xml.sax.*;
import org.xml.sax.helpers.*;

public class TestSAX2
{
  
  public static void main(String[] args)
  {
    try
    {
      Class c = Class.forName("org.apache.xerces.parsers.SAXParser");
      XMLReader reader = (XMLReader)c.newInstance();
      TestSAX2Handler handler = new TestSAX2Handler();
      reader.setContentHandler(handler);
      reader.parse("test.xml");
    }
    catch(Exception e){System.out.println(e);}
  }

}

class TestSAX2Handler extends DefaultHandler
{
  private String tagCourant = "";
  
  /**
   * Actions à réaliser lors de la détection d'un nouvel élément.
   */
  public void startElement(String nameSpace, String localName, 
    String qName, Attributes attr) throws SAXException  {
    tagCourant = localName;
    System.out.println("debut tag : " + localName);
  }

  /**
   * Actions à réaliser lors de la détection de la fin d'un élément.
   */
  public void endElement(String nameSpace, String localName, 
    String qName) throws SAXException {
    tagCourant = "";
    System.out.println("Fin tag " + localName);
  }

  /**
   * Actions à réaliser au début du document.
   */
  public void startDocument() {
    System.out.println("Debut du document");
  }

  /**
   * Actions à réaliser lors de la fin du document XML.
   */
  public void endDocument() {
    System.out.println("Fin du document");
  }

  /**
   * Actions à réaliser sur les données
   */
  public void characters(char[] caracteres, int debut, 
    int longueur) throws SAXException {
    String donnees = new String(caracteres, debut, longueur);

    if (!tagCourant.equals("")) {	
      if(!Character.isISOControl(caracteres[debut])) {
        System.out.println("   Element " + tagCourant +", 
          valeur = *" + donnees + "*");
      }
    }
  }
}

 


Développons en Java v 2.20   Copyright (C) 1999-2021 Jean-Michel DOUDOUX.   
[ Précédent ] [ Sommaire ] [ Suivant ] [ Télécharger ]      [ Accueil ] [ Commentez ]