Programmation XML en Java, partie 1

Ainsi, vous comprenez (plus ou moins) comment vous représenteriez vos données en XML, et vous êtes intéressé par l'utilisation de XML pour résoudre nombre de vos problèmes de gestion de données. Pourtant, vous ne savez pas comment utiliser XML avec vos programmes Java.

TEXTBOX: TEXTBOX_HEAD: Programmation XML en Java: Lisez toute la série!

  • Partie 1. Utilisez l'API simple pour XML (SAX) pour traiter facilement XML en Java
  • Partie 2. En savoir plus sur la validation SAX et XML à travers des exemples illustratifs
  • Partie 3. DOMination: Prenez le contrôle de documents structurés avec le modèle d'objet de document

: END_TEXTBOX

Cet article fait suite à mon article d'introduction, "XML pour le débutant absolu", dans le numéro d'avril 1999 de JavaWorld (voir la section Ressources ci-dessous pour l'URL). Cet article décrivait XML; Je vais maintenant m'appuyer sur cette description et montrer en détail comment créer une application qui utilise l'API simple pour Java (SAX), une API Java standard légère et puissante pour le traitement de XML.

L'exemple de code utilisé ici utilise l'API SAX pour lire un fichier XML et créer une structure d'objets utile. Au moment où vous aurez terminé cet article, vous serez prêt à créer vos propres applications XML.

La vertu de la paresse

Larry Wall, le génie fou créateur de Perl (le deuxième plus grand langage de programmation existant), a déclaré que la paresse est l'une des "trois grandes vertus" d'un programmeur (les deux autres étant l'impatience et l'orgueil). La paresse est une vertu parce qu'un programmeur paresseux ira à presque n'importe quelle longueur pour éviter le travail, allant même jusqu'à créer des cadres de programmation généraux et réutilisables qui peuvent être utilisés à plusieurs reprises. La création de tels cadres implique beaucoup de travail, mais le temps gagné sur les futures missions compense largement l'effort initial investi. Les meilleurs frameworks permettent aux programmeurs de faire des choses incroyables avec peu ou pas de travail - et c'est pourquoi la paresse est vertueuse.

XML est une technologie habilitante pour le programmeur vertueux (paresseux). Un analyseur XML de base fait beaucoup de travail pour le programmeur, en reconnaissant les jetons, en traduisant les caractères codés, en appliquant les règles sur la structure des fichiers XML, en vérifiant la validité de certaines valeurs de données et en faisant des appels au code spécifique à l'application, le cas échéant. En fait, au début de la normalisation, combinée à un marché très concurrentiel, a produit des dizaines de librement implémentations de parseurs XML standard dans de nombreuses langues disponibles, y compris C, C ++, Tcl, Perl, Python, et, bien sûr, Java.

L'API SAX est l'une des interfaces les plus simples et les plus légères pour gérer XML. Dans cet article, j'utiliserai l'implémentation XML4J d'IBM de SAX, mais puisque l'API est normalisée, votre application pourrait remplacer n'importe quel package qui implémente SAX.

SAX est une API basée sur les événements, fonctionnant sur le principe du rappel. Un programmeur d'application crée généralement un Parserobjet SAX et lui transmet à la fois le XML d'entrée et un gestionnaire de documents, qui reçoit des rappels pour les événements SAX. Le SAX Parserconvertit son entrée en un flux d' événements correspondant à des caractéristiques structurelles de l'entrée, telles que des balises XML ou des blocs de texte. Au fur et à mesure que chaque événement se produit, il est passé à la méthode appropriée d'un gestionnaire de documents défini par le programmeur, qui implémente l'interface de rappel org.xml.sax.DocumentHandler. Les méthodes de cette classe de gestionnaire exécutent les fonctionnalités spécifiques à l'application pendant l'analyse.

Par exemple, imaginez qu'un analyseur SAX reçoive un document contenant le petit document XML présenté dans la liste 1 ci-dessous. (Voir Ressources pour le fichier XML.)

 Ogden Nash Fleas Adam les avait.  

Listing 1. XML représentant un court poème

Lorsque l'analyseur SAX rencontre la balise, il appelle la valeur définie DocumentHandler.startElement()par l' utilisateur avec la chaîne POEMcomme argument. Vous implémentez la startElement()méthode pour faire tout ce que l'application est censée faire au début d'un POEM. Le flux d'événements et les appels résultants pour l'élément XML ci-dessus apparaissent dans le tableau 1 ci-dessous.

Tableau 1. La séquence des rappels produits par SAX lors de l'analyse du Listing 1
Élément rencontré Rappel d'analyseur
{Début du document} startDocument()
startElement("POEM", {AttributeList})
"\ n" characters("\n...", 6, 1)
startElement("AUTHOR", {AttributeList})
«Ogden Nash» characters("\n...", 15, 10)
endElement("AUTHOR")
"\ n" characters("\n...", 34, 1)
startElement("TITLE", {AttributeList})
"Des puces" characters("\n...", 42, 5)
endElement("TITLE")
"\ n" characters("\n...", 55, 1)
startElement("LINE", {AttributeList})
"Adam" characters("\n...", 62, 4)
endElement("LINE")
startElement("LINE", {AttributeList})
"Je les avais." characters("\n...", 67, 8)
endElement("LINE")
"\ n" characters("\n...", 82, 1)
endElement("POEM")
{Fin du document} endDocument()

Vous créez une classe qui implémente DocumentHandlerpour répondre aux événements qui se produisent dans l'analyseur SAX. Ces événements ne sont pas des événements Java tels que vous les connaissez peut-être dans le Abstract Windowing Toolkit (AWT). Ce sont des conditions que l'analyseur SAX détecte lors de son analyse, comme le début d'un document ou l'occurrence d'une balise de fermeture dans le flux d'entrée. Lorsque chacune de ces conditions (ou événements) se produit, SAX appelle la méthode correspondant à la condition dans son DocumentHandler.

Donc, la clé pour écrire des programmes qui traitent XML avec SAX est de déterminer ce que DocumentHandlerdoit faire en réponse à un flux de rappels de méthode de SAX. L'analyseur SAX s'occupe de tous les mécanismes d'identification des balises, de substitution des valeurs d'entité, etc., vous laissant libre de vous concentrer sur la fonctionnalité spécifique à l'application qui utilise les données encodées dans le XML.

Le tableau 1 montre uniquement les événements associés aux éléments et aux caractères. SAX comprend également des fonctionnalités permettant de gérer d'autres fonctionnalités structurelles des fichiers XML, telles que les entités et les instructions de traitement, mais celles-ci sortent du cadre de cet article.

Le lecteur avisé remarquera qu'un document XML peut être représenté comme une arborescence d'objets typés, et que l'ordre du flux d'événements présenté au DocumentHandlercorrespond à une traversée dans l'ordre, en profondeur d'abord de l'arborescence du document. (Il n'est pas essentiel de comprendre ce point, mais le concept d'un document XML en tant que structure de données arborescente est utile dans des types plus sophistiqués de traitement de documents, qui seront traités dans les articles ultérieurs de cette série.)

La clé pour comprendre comment utiliser SAX est de comprendre l' DocumentHandlerinterface, dont je parlerai ensuite.

Personnalisez l'analyseur avec org.xml.sax.DocumentHandler

Since the DocumentHandler interface is so central to processing XML with SAX, it's worthwhile to understand what the methods in the interface do. I'll cover the essential methods in this section, and skip those that deal with more advanced topics. Remember, DocumentHandler is an interface, so the methods I'm describing are methods that you will implement to handle application-specific functionality whenever the corresponding event occurs.

Document initialization and cleanup

For each document parsed, the SAX XML parser calls the DocumentHandler interface methods startDocument() (called before processing begins) and endDocument() (called after processing is complete). You can use these methods to initialize your DocumentHandler to prepare it for receiving events and to clean up or produce output after parsing is complete. endDocument() is particularly interesting, since it's only called if an input document has been successfully parsed. If the Parser generates a fatal error, it simply aborts the event stream and stops parsing, and endDocument() is never called.

Processing tags

The SAX parser calls startElement() whenever it encounters an open tag, and endElement() whenever it encounters a close tag. These methods often contain the code that does the majority of the work while parsing an XML file. startElement()'s first argument is a string, which is the tag name of the element encountered. The second argument is an object of type AttributeList, an interface defined in package org.xml.sax that provides sequential or random access to element attributes by name. (You've undoubtedly seen attributes before in HTML; in the line

BORDER

Since SAX doesn't provide any information about the context of the elements it encounters (that appears inside in Listing 1 above, for example), it is up to you to supply that information. Application programmers often use stacks in startElement() and endElement(), pushing objects onto a stack when an element starts, and popping them off of the stack when the element ends.

Process blocks of text

The characters() method indicates character content in the XML document -- characters that don't appear inside an XML tag, in other words. This method's signature is a bit odd. The first argument is an array of bytes, the second is an index into that array indicating the first character of the range to be processed, and the third argument is the length of the character range.

It might seem that an easier API would have simply passed a String object containing the data, but characters() was defined in this way for efficiency reasons. The parser has no way of knowing whether or not you're going to use the characters, so as the parser parses its input buffer, it passes a reference to the buffer and the indices of the string it is viewing, trusting that you will construct your own String if you want one. It's a bit more work, but it lets you decide whether or not to incur the overhead of String construction for content pieces in an XML file.

The characters() method handles both regular text content and content inside CDATA sections, which are used to prevent blocks of literal text from being parsed by an XML parser.

Other methods

Il y a trois autres méthodes dans l' DocumentHandlerinterface: ignorableWhitespace(), processingInstruction()et setDocumentLocator(). ignorableWhitespace()signale les occurrences d'espaces blancs et n'est généralement pas utilisé dans les analyseurs SAX non validants (comme celui que nous utilisons pour cet article); processingInstruction()gère la plupart des choses à l'intérieur and ?> delimiters; and setDocumentLocator() is optionally implemented by SAX parsers to give you access to the locations of SAX events in the original input stream. You can read up on these methods by following the links on the SAX interfaces in Resources.

Implementing all of the methods in an interface can be tedious if you're only interested in the behavior of one or two of them. The SAX package includes a class called HandlerBase that basically does nothing, but can help you take advantage of just one or two of these methods. Let's examine this class in more detail.

HandlerBase: A do-nothing class

Often, you're only interested in implementing one or two methods in an interface, and want the other methods to simply do nothing. The class org.xml.sax.HandlerBase simplifies the implementation of the DocumentHandler interface by implementing all of the interface's methods with do-nothing bodies. Then, instead of implementing DocumentHandler, you can subclass HandlerBase, and only override the methods that interest you.

For example, say you wanted to write a program that just printed the title of any XML-formatted poem (like TitleFinder in Listing 1). You could define a new DocumentHandler, like the one in Listing 2 below, that subclasses HandlerBase, and only overrides the methods you need. (See Resources for an HTML file of TitleFinder.)

012 /** 013 * SAX DocumentHandler class that prints the contents of "TITLE" element 014 * of an input document. 015 */ 016 public class TitleFinder extends HandlerBase { 017 boolean _isTitle = false; 018 public TitleFinder() { 019 super(); 020 } 021 /** 022 * Print any text found inside a  element. 023 */ 024 public void characters(char[] chars, int iStart, int iLen) { 025 if (_isTitle) { 026 String sTitle = new String(chars, iStart, iLen); 027 System.out.println("Title: " + sTitle); 028 } 029 } 030 /** 031 * Mark title element end. 032 */ 033 public void endElement(String element) { 034 if (element.equals("TITLE")) { 035 _isTitle = false; 036 } 037 } 038 /** 039 * Find contents of titles 040 */ 041 public static void main(String args[]) { 042 TitleFinder titleFinder = new TitleFinder(); 043 try { 044 Parser parser = ParserFactory.makeParser("com.ibm.xml.parsers.SAXParser"); 045 parser.setDocumentHandler(titleFinder); 046 parser.parse(new InputSource(args[0])); 047 } catch (Exception ex) { 048 ; // OK, so sometimes laziness *isn't* a virtue. 049 } 050 } 051 /** 052 * Mark title element start 053 */ 054 public void startElement(String element, AttributeList attrlist) { 055 if (element.equals("TITLE")) { 056 _isTitle = true; 057 } 058 } 

Listing 2. TitleFinder: A DocumentHandler derived from HandlerBase that prints TITLEs


#####


, est un attribut dont la valeur est "1"). Étant donné que le listing 1 n'inclut aucun attribut, ils n'apparaissent pas dans le tableau 1. Vous verrez des exemples d'attributs dans l'exemple d'application plus loin dans cet article.