Programmazione XML in Java, parte 1

Quindi, comprendi (più o meno) come rappresenteresti i tuoi dati in XML e sei interessato a utilizzare XML per risolvere molti dei tuoi problemi di gestione dei dati. Eppure non sei sicuro di come utilizzare XML con i tuoi programmi Java.

TEXTBOX: TEXTBOX_HEAD: Programmazione XML in Java: leggi l'intera serie!

  • Parte 1. Utilizzare l'API semplice per XML (SAX) per elaborare facilmente XML in Java
  • Parte 2. Informazioni sulla convalida SAX e XML tramite esempi illustrativi
  • Parte 3. Dominio: assumi il controllo dei documenti strutturati con il Document Object Model

: END_TEXTBOX

Questo articolo è il seguito del mio articolo introduttivo, "XML per il principiante assoluto", nel numero di aprile 1999 di JavaWorld (vedere la sezione Risorse di seguito per l'URL). Quell'articolo descriveva XML; Ora mi baserò su questa descrizione e mostrerò in dettaglio come creare un'applicazione che utilizzi Simple API for Java (SAX), un'API Java standard leggera e potente per l'elaborazione XML.

Il codice di esempio qui utilizzato utilizza l'API SAX per leggere un file XML e creare un'utile struttura di oggetti. Quando avrai finito questo articolo, sarai pronto per creare le tue applicazioni basate su XML.

La virtù della pigrizia

Larry Wall, genio pazzo creatore di Perl (il secondo più grande linguaggio di programmazione esistente), ha affermato che la pigrizia è una delle "tre grandi virtù" di un programmatore (le altre due sono l'impazienza e l'arroganza). La pigrizia è una virtù perché un programmatore pigro farà di tutto per evitare il lavoro, arrivando anche alla creazione di framework di programmazione generali e riutilizzabili che possono essere utilizzati ripetutamente. La creazione di tali quadri comporta una grande mole di lavoro, ma il tempo risparmiato su incarichi futuri compensa ampiamente lo sforzo iniziale investito. I migliori framework consentono ai programmatori di fare cose sorprendenti con poco o nessun lavoro - ed è per questo che la pigrizia è virtuosa.

XML è una tecnologia abilitante per il programmatore virtuoso (pigro). Un parser XML di base fa molto lavoro per il programmatore, riconoscendo i token, traducendo caratteri codificati, applicando regole sulla struttura dei file XML, controllando la validità di alcuni valori di dati ed effettuando chiamate al codice specifico dell'applicazione, ove appropriato. In effetti, la standardizzazione iniziale, combinata con un mercato estremamente competitivo, ha prodotto decine di implementazioni disponibili gratuitamente di parser XML standard in molti linguaggi, inclusi C, C ++, Tcl, Perl, Python e, ovviamente, Java.

L'API SAX è una delle interfacce più semplici e leggere per la gestione di XML. In questo articolo, userò l'implementazione XML4J di SAX di IBM, ma poiché l'API è standardizzata, la tua applicazione potrebbe sostituire qualsiasi pacchetto che implementa SAX.

SAX è un'API basata su eventi, che opera sul principio di callback. Un programmatore di applicazioni creerà in genere un Parseroggetto SAX e gli passerà sia l'XML di input che un gestore di documenti, che riceve i callback per gli eventi SAX. Il SAX Parserconverte il suo input in un flusso di eventi corrispondenti alle caratteristiche strutturali dell'input, come tag XML o blocchi di testo. Quando si verifica ogni evento, viene passato al metodo appropriato di un gestore di documenti definito dal programmatore, che implementa l'interfaccia di callback org.xml.sax.DocumentHandler. I metodi in questa classe di gestore eseguono la funzionalità specifica dell'applicazione durante l'analisi.

Ad esempio, immagina che un parser SAX riceva un documento contenente il piccolo documento XML mostrato nel Listato 1 di seguito. (Vedi Risorse per il file XML.)

 Ogden Nash Pulci Adam li aveva.  

Listato 1. XML che rappresenta una breve poesia

Quando il parser SAX incontra il tag, chiama quello definito dall'utente DocumentHandler.startElement()con la stringa POEMcome argomento. Si implementa il startElement()metodo per fare tutto ciò che l'applicazione deve fare quando POEMinizia a. Il flusso di eventi e le chiamate risultanti per il pezzo di XML sopra appare nella Tabella 1 sotto.

Tabella 1. La sequenza di callback che SAX produce durante l'analisi del Listato 1
Oggetto riscontrato Richiamata parser
{Inizio del documento} 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})
"Pulci" characters("\n...", 42, 5)
endElement("TITLE")
"\ n" characters("\n...", 55, 1)
startElement("LINE", {AttributeList})
"Adamo" characters("\n...", 62, 4)
endElement("LINE")
startElement("LINE", {AttributeList})
"Li avevo." characters("\n...", 67, 8)
endElement("LINE")
"\ n" characters("\n...", 82, 1)
endElement("POEM")
{Fine del documento} endDocument()

Si crea una classe che implementa DocumentHandlerper rispondere agli eventi che si verificano nel parser SAX. Questi eventi non sono eventi Java come potresti conoscerli da Abstract Windowing Toolkit (AWT). Sono condizioni che il parser SAX rileva durante l'analisi, come l'inizio di un documento o l'occorrenza di un tag di chiusura nel flusso di input. Quando si verifica ciascuna di queste condizioni (o eventi), SAX chiama il metodo corrispondente alla condizione nel suo file DocumentHandler.

Quindi, la chiave per scrivere programmi che elaborano XML con SAX è capire cosa DocumentHandlerdovrebbe fare in risposta a un flusso di callback di metodo da SAX. Il parser SAX si occupa di tutti i meccanismi di identificazione dei tag, sostituzione dei valori di entità e così via, lasciandoti libero di concentrarti sulla funzionalità specifica dell'applicazione che utilizza i dati codificati nell'XML.

La tabella 1 mostra solo gli eventi associati a elementi e personaggi. SAX include anche funzionalità per la gestione di altre caratteristiche strutturali dei file XML, come entità e istruzioni di elaborazione, ma queste vanno oltre lo scopo di questo articolo.

Il lettore astuto noterà che un documento XML può essere rappresentato come un albero di oggetti digitati, e che l'ordine del flusso di eventi presentato a DocumentHandlercorrisponde a un attraversamento in ordine e in profondità dell'albero del documento. (Non è essenziale comprendere questo punto, ma il concetto di un documento XML come struttura di dati ad albero è utile nei tipi più sofisticati di elaborazione dei documenti, che saranno trattati negli articoli successivi di questa serie.)

La chiave per capire come usare SAX è capire l' DocumentHandlerinterfaccia, di cui parlerò in seguito.

Personalizza il parser con org.xml.sax.DocumentHandler

Poiché l' DocumentHandlerinterfaccia è così centrale per l'elaborazione di XML con SAX, vale la pena capire cosa fanno i metodi nell'interfaccia. Tratterò i metodi essenziali in questa sezione e tralascerò quelli che trattano argomenti più avanzati. Ricorda, DocumentHandlerè un'interfaccia, quindi i metodi che sto descrivendo sono metodi che implementerai per gestire funzionalità specifiche dell'applicazione ogni volta che si verifica l'evento corrispondente.

Inizializzazione e pulizia dei documenti

Per ogni documento analizzato, il parser XML SAX chiama i DocumentHandlermetodi dell'interfaccia startDocument()(chiamati prima dell'inizio dell'elaborazione) e endDocument()(chiamati dopo che l'elaborazione è stata completata). È possibile utilizzare questi metodi per inizializzarlo DocumentHandlerper prepararlo alla ricezione di eventi e per ripulire o produrre output al termine dell'analisi. endDocument()è particolarmente interessante, poiché viene chiamato solo se un documento di input è stato analizzato con successo. Se Parsergenera un errore irreversibile, interrompe semplicemente il flusso di eventi e interrompe l'analisi e endDocument()non viene mai chiamato.

Tag di elaborazione

Il parser SAX chiama startElement()ogni volta che incontra un tag aperto e endElement()ogni volta che incontra un tag di chiusura. Questi metodi spesso contengono il codice che svolge la maggior parte del lavoro durante l'analisi di un file XML. startElement()Il primo argomento è una stringa, che è il nome del tag dell'elemento incontrato. Il secondo argomento è un oggetto di tipo AttributeList, un'interfaccia definita nel pacchetto org.xml.saxche fornisce un accesso sequenziale o casuale agli attributi dell'elemento in base al nome. (Indubbiamente hai già visto attributi in HTML; nella riga

BORDER

Poiché SAX non fornisce alcuna informazione sul contesto degli elementi che incontra (che appare all'interno del Listato 1 sopra, per esempio), spetta a te fornire tali informazioni. I programmatori di applicazioni spesso utilizzano gli stack in startElement()e endElement(), inserendo gli oggetti in uno stack quando un elemento inizia e estraendoli dallo stack quando l'elemento termina.

Elabora blocchi di testo

Il characters()metodo indica il contenuto dei caratteri nel documento XML, in altre parole caratteri che non compaiono all'interno di un tag XML. La firma di questo metodo è un po 'strana. Il primo argomento è un array di byte, il secondo è un indice in quell'array che indica il primo carattere dell'intervallo da elaborare e il terzo argomento è la lunghezza dell'intervallo di caratteri.

Potrebbe sembrare che un'API più semplice avrebbe semplicemente passato un Stringoggetto contenente i dati, ma è characters()stata definita in questo modo per ragioni di efficienza. Il parser non ha modo di sapere se utilizzerai o meno i caratteri, quindi mentre analizza il suo buffer di input, passa un riferimento al buffer e agli indici della stringa che sta visualizzando, fidandosi che costruirai il tuo Stringse ne vuoi uno. È un po 'più di lavoro, ma ti consente di decidere se sostenere o meno il sovraccarico di Stringcostruzione per parti di contenuto in un file XML.

Il characters()metodo gestisce sia il contenuto di testo normale che il contenuto all'interno delle sezioni CDATA, che vengono utilizzate per impedire che blocchi di testo letterale vengano analizzati da un parser XML.

Altri metodi

Ci sono altri tre metodi nel DocumentHandlerdell'interfaccia: ignorableWhitespace(), processingInstruction(), e setDocumentLocator(). ignorableWhitespace()segnala le occorrenze di spazi vuoti e di solito non è utilizzato in parser SAX non convalidati (come quello che stiamo usando per questo articolo); processingInstruction()gestisce la maggior parte delle cose all'interno 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


#####


, è un attributo il cui valore è "1"). Poiché il Listato 1 non include attributi, non vengono visualizzati nella Tabella 1. Più avanti in questo articolo verranno visualizzati esempi di attributi nell'applicazione di esempio.