XMLBeans è una tecnologia che consente di manipolare documenti XML attraverso dei JavaBeans generati a partire dallo schema che definisce il documento. In questo articolo si mostrerà come definire un XSD e come utilizzare XMLBeans.
Introduzione
I documenti in formato XML (eXtensible Markup Language) sono oggi lo standard de facto per l‘interscambio di dati tra applicazioni che, pur parlando una lingua diversa, hanno bisogno di comunicare. La possibilità di definirne una struttura “object oriented”, associandoli ad un XSD (XML Schema Definition, Schema di Definizione del XML), li rende poi particolarmente adatti ad essere gestiti servendosi di java beans che ne preservino la conformazione e di oggetti che contengano la logica per verificarne la coerenza rispetto a quanto specificato nello schema di definizione.
XMLBeans è una tecnologia distribuita con licenza open source dall‘Apache Software Foundation che consente di manipolare documenti xml attraverso dei java beans generati a partire dallo schema che definisce il documento.
In questo articolo si mostrerà come definire un XSD e come utilizzare XMLBeans per generare i java beans ad esso associati e da questi plasmare un documento xml “well-formed” e che rispetti le regole definite nel suo schema.
Schema di definizione XML: introduzione agli XSD
Un documento XML è costituito da blocchi o frammenti composti da elementi ed attributi. Il seguente blocco, ad esempio
Londra
è composto da 2 elementi, e . L‘elemento ha come attributo il numeroDiGiorni, il quale è valorizzato con un numero intero indicante la durata in giorni della tappa e contiene al suo interno l‘elemento , il quale è valorizzato con il nome della meta della tappa.
In un XSD si definisce come costruire in modo legale i singoli blocchi che formano un documento XML e cioè
- gli elementi e gli attributi che si possono inserire nel documento;
- la gerarchia che intercorre tra i diversi elementi;
- l‘ordine che un elemento può assumere nella gerarchia;
- quando un elemento può non assumere alcun valore e quando invece deve essere popolato obbligatoriamente;
- definire il tipo di dato per un elemento o un attributo;
- definire valori di default o valori costanti per un elemento o un attributo.
Questi concetti basilari si possono spiegare meglio con un esempio.
Si vuole costruire un documento xml che contenga le informazioni necessarie per rappresentare il percorso effettuato da un viaggiatore nel suo viaggio. Il viaggio può esser definito nell‘ordine dal viaggiatore, dalla data di partenza, dalla data di ritorno e da una o più tappe. Del viaggiatore interessano, in sequenza, il nome e il cognome, della tappa il nome della città ed eventualmente il numero di giorni per cui intende fermarsi.
Ecco uno schema di definizione che ben si adatta come soluzione per il problema.
xmlns_ar="https://www.mokabyte.it/viaggio" targetNamespace="https://www.mokabyte.it/viaggio" elementFormDefault="qualified"> use="optional" /> maxOccurs="unbounded" />
I punti da sottolineare per questo esempio riportati si seguito
Un tipo di dato, cioè il valore presente nell‘attributo type di un elemento o di un attributo può essere Predefinito (interi, stringhe, date e tutti gli altri tipi già definiti dal W3C, World Wide Web Consortium) oppure Semplice (tipi definiti dall‘utente all‘interno di un tag ) oppure Complesso (i tipi definiti dall‘utente all‘interno di un tag , come è il tipo utilizzato nell‘esempio per definire un viaggiatore). Di seguito riportiamo come esempio un tipo semplice che impone a un codice fiscale di essere una stringa di 16 caratteri:
Il tipo di dato che può assumere un elemento può far parte dei tipi predefiniti dallo schema, è il caso delle date di partenza e di arrivo, o dei tipi definiti dall‘utente, come avviene ad esempio per viaggiatore e tappa, siano essi semplici o complessi.
Il tipo di dato che può assumere un attributo può far parte dei tipi predefiniti dallo schema o dei tipi semplici definiti dall‘utente.
Quando un tipo complesso è definito come sequenza di più elementi, cioè quando all‘interno di un tag è presente un tag , gli elementi che costituiscono la sequenza devono essere posizionati all‘interno di un blocco xml rispettando l‘ordinamento con cui è stata definita la sequenza.
È possibile specificare l‘obbligatorietà di un attributo valorizzando opportunamente il campo use: se popolato con required, è necessario specificare un valore per l‘attributo, se popolato con optional, il valore si può omettere.
Per indicare il numero di volte che un elemento può essere presente in una gerarchia, si utilizzano gli attributi minOccurs e maxOccurs. Nell‘esempio, l‘elemento tappa deve essere presente almeno una volta e può essere presente per un numero di volte indefinito. Quando tali attributi non sono specificati, significa che l‘elemento deve essere presente una ed una sola volta.
Ulteriori approfondimenti sono disponibili sul tutorial on-line del W3C [1].
Generazione degli XML Beans con ANT
Dopo aver creato un “semplice progetto java” ed aver incluso in esso le librerie di XMLBeans comprese nella distribuzione che si può liberamente scaricare dal sito web del prodotto [2], si può procedere con la generazione dei java beans da utilizzare per manipolare i documenti xml associati allo schema di definizione descritto nel paragrafo precedente.
A tal proposito, XMLBeans mette a disposizione degli sviluppatori un target ANT chiamato .
Come mostrato di seguito, è sufficiente indicare a tale target il in cui si trova lo schema di definizione, il classpath in cui sono posizionate le librerie di XMLBeans, il nome e il percorso del file jar di destinazione ed eventualmente il percorso in cui generare il codice sorgente.
description="generates the xml java beans from xsd"> destfile="${viaggio-xmlbeans.libs.dir}/viaggio-xmlbeans.jar" classpathref="xmlbeans.path">
Nel target precedente è stato incluso anche il seguente file di configurazione viaggio.xsdconfig, in cui si specifica il nome del package in cui generare i java beans.
<xb:config xmlns_xb="http://xml.apache.org/xmlbeans/2004/02/xbean/config" xmlns_ar="https://www.mokabyte.it/viaggio"> it.mokabyte.viaggio.xmlbeans
Il risultato di questa operazione è la generazione di quattro interfacce con relative implementazioni:
- L‘interfaccia ViaggioDocument, contenente i metodi per accedere ai blocchi inclusi nell‘elemento alla radice del documento xml.
- L‘interfaccia Viaggio contenente i metodi per accedere ad elementi ed attributi del tipo complesso , ergo ai dati realativi al viaggio.
- L‘interfaccia Viaggiatore contenente i metodi per accedere ad elementi ed attributi del tipo complesso , ergo ai dati relativi al viaggiatore.
- L‘interfaccia Tappa contenente i metodi per accedere ad elementi ed attributi del tipo complesso , ergo ai dati relativi alla tappa.
Tali interfacce estendono tutte l‘interfaccia XMLObject, con la quale XMLBeans definisce i metodi per la lettura, l‘accesso ai dati , la validazione e la scrittura di un documento xml (figura 1).
Figura 1 – Le 4 interfacce generate
Lettura di un file XML
Generati i JavaBeans, si può ora leggere il seguente documento e tradurne il contenuto all‘interno dell‘oggetto ViaggioDocument. Il viaggiatore è il signor Ciccio Macri, il quale partirà il 3 agosto prossimo venturo per un viaggio di quattro tappe: Vienna, Berlino, Amsterdam e Londra, avendo prefissato di soggiornare per tre giorni a Londra e per due ad Amsterdam. Indefinita è invece la permanenza a Berlino e Vienna. Il ritorno è previsto per il 13 agosto.
xmlns_xsi="http://www.w3.org/2001/XMLSchema-instance" xsi_schemaLocation="https://www.mokabyte.it/viaggio viaggio.xsd "> Ciccio Macri 2007-08-03 2007-08-13 Vienna Berlino Amsterdam Londra
Il seguente metodo, dato il percorso del file xml, permette di ottenere un oggetto di tipo ViaggioDocument contenente al suo interno tutte le informazioni relative al viaggio descritto nel file XML.
public ViaggioDocument readXML(String xmlPath) throws XmlException, IOException{ File xmlFile = new File(xmlPath); XmlOptions options = new XmlOptions(); options.setLoadLineNumbers(XmlOptions.LOAD_LINE_NUMBERS_END_ELEMENT); ViaggioDocument viaggioDoc = ViaggioDocument.Factory.parse(xmlFile, options); return viaggioDoc; }
Da evidenziare che, per ottenere il document-bean , l‘interfaccia ViaggioDocument stessa mette a disposizione il metodo parse che traduce il file xml in java bean. Tale metodo si può invocare, oltre che con il solo parametro di tipo File contenente il file da convertire, con un ulteriore parametro di tipo XMLOptions, contenente al suo interno le opzioni settabili per migliorare le operazioni di traduzione; nell‘esempio si è scelto di caricare nell‘oggetto ViaggioDocument anche i numeri di linea dei singoli componenti del documento xml, i quali, come si vedrà in seguito, sono molto comodi per individuare eventuali errori contenuti nella struttura o nei dati del documento.
Accesso ai dati e popolamento di un XML bean
Una volta incapsulati i dati all‘interno dell‘oggetto ViaggioDocument, si possono leggere e modificare le informazioni contenute nel file xml semplicemente utilizzando i “getter” e “setter” del bean.
Nel metodo seguente, evitando di sporcare il documento originale, si effettuano delle modifiche al viaggio, posticipando la data di ritorno di tre giorni e aggiungendo una nuova tappa: Parigi.
public ViaggioDocument modifyXML(ViaggioDocument viaggioDoc) { ViaggioDocument result = (ViaggioDocument) viaggioDoc.copy(); Calendar dataRitorno = result.getViaggio().getDataRitorno(); dataRitorno.add(Calendar.DAY_OF_MONTH, 3); result.getViaggio().setDataRitorno(dataRitorno); Tappa tappa = result.getViaggio().addNewTappa(); tappa.setCitta("Parigi"); return result; }
Generazione di un documento XML
Dopo aver modificato i dati all‘interno del Document-bean, si può salvare il percorso aggiornato su un nuovo documento xml.
Il metodo seguente sfrutta il metodo save del ViaggioDocument per riversare i dati dal bean ad un file xml.
public void writeXML(ViaggioDocument viaggioDoc, String xmlPath) throws IOException{ File ouputFile = new File(xmlPath); XmlOptions options = new XmlOptions(); options.setSavePrettyPrint(); viaggioDoc.save(ouputFile,options); }
L‘opzione setSavePrettyPrint si utilizza per preservare un‘indentatura che migliori la leggibilità del documento xml che si sta generando.
Ecco il documento che si ottiene al termine di questa operazione:
xmlns_ar="https://www.mokabyte.it/viaggio" xmlns_xsi="http://www.w3.org/2001/XMLSchema-instance"> Ciccio Macri 2007-08-03 2007-08-16+02:00 Vienna Berlino Amsterdam Londra Parigi
Validazione
Per verificare se la struttura e i dati del documento xml sono coerenti con quanto specificato nello schema di definizione, l‘interfaccia ViaggioDocument mette a disposizione il metodo validate.
public void validateXML(ViaggioDocument viaggioDoc) throws XmlException { boolean isValid; Collection errors = new ArrayList(); XmlOptions options = new XmlOptions(); xo.setErrorListener(errors); isValid = viaggioDoc.validate(options); if (!isValid) { XmlException e = new XmlException("Validation errors", null, errors); throw e; } }
Per tenere traccia degli eventuali errori riscontrati in fase di validazione, si può attivare l‘opzione setErrorListener, la quale si occupa di aggiungere ad una collezione di errori un oggetto di tipo XmlError per ogni mancata conformità del file xml rispetto a quanto definito nello schema.
All‘interno dell‘oggetto di tipo XmlError si può risalire facilmente ad informazioni importanti per identificare l‘errore quali:
- la linea del file in cui è presente l‘errore
- il messaggio di errore
- l‘elemento o l‘attributo alla base dell‘errore
Nel seguente metodo si può vedere come stampare informazioni relative agli errori nel momento in cui si riceve un‘eccezione di tipo XmlException durante la validazione.
public static void printErrors(XmlException e){ Iterator errorsIterator = e.getErrors().iterator(); while(errorsIterator.hasNext()){ XmlError error = (XmlError) errorsIterator.next(); System.err.println("Line "+ error.getLine()+": "+error.getMessage()); } }
Da notare che il numero di linea in cui si è verificato l‘errore è disponibile solo nel caso in cui in fase di lettura del file contenente l‘errore è stata attivata l‘opzione setLoadLineNumbers e se, in seguito ad eventuali modifiche del documento originale, il documento da validare è stato generato come indicato nel paragrafo precedente.
Conclusioni
In questo articolo, dopo un‘introduzione agli xsd, si è visto come utilizzare la tecnologia XMLBeans per maneggiare con semplici java beans il contenuto di documenti in formato xml. In particolare sono state esaminate le operazioni di lettura del documento, accesso e modifica ai dati, scrittura su file e verifica della conformità del documento rispetto al suo schema di definizione.
Riferimenti
[1] XML Schema Tutorial
http://www.w3schools.com/schema
[2] XMLBeans Site
http://xmlbeans.apache.org