Niveau : | 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 :
SAX type 1 est composé de deux packages :
SAX définit plusieurs classes et interfaces :
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 :
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 :
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 :
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 :
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
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.
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 :
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 + "*");
}
}
}
}