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
<tappa numeroDiGiorni="4"> <citta>Londra</citta> </tappa> è composto da 2 elementi, <tappa> e <citta>. L'elemento <tappa> 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 <citta>, 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è <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:ar="http://www.mokabyte.it/viaggio" targetNamespace="http://www.mokabyte.it/viaggio" elementFormDefault="qualified"> <xs:complexType name="viaggiatore"> <xs:sequence> <xs:element name="nome" type="xs:string" /> <xs:element name="cognome" type="xs:string" /> </xs:sequence>
</xs:complexType>
<xs:complexType name="tappa"> <xs:sequence> <xs:element name="citta" type="xs:string" /> </xs:sequence> <xs:attribute name="numeroDiGiorni" type="xs:integer" use="optional" /> </xs:complexType> <xs:complexType name="viaggio"> <xs:sequence> <xs:element name="viaggiatore" type="ar:viaggiatore" /> <xs:element name="dataPartenza" type="xs:date" /> <xs:element name="dataRitorno" type="xs:date" /> <xs:element name="tappa" type="ar:tappa" minOccurs="1" maxOccurs="unbounded" /> </xs:sequence> </xs:complexType> <xs:element name="viaggio" type="ar:viaggio"/>
</xs:schema> ## 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 <xs:simpleType>) oppure Complesso (i tipi definiti dall'utente all'interno di un tag <xs:complexType>, 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: <xs:simpleType name="codiceFiscaleType"> <xs:restriction base="xs:string"> <xs:length value="16"></xs:length> </xs:restriction> </xs:simpleType> Quando un tipo complesso è definito come sequenza di più elementi, cioè quando all'interno di un tag <xs:complexType> è presente un tag <xs:sequence>, gli elementi che costituiscono la sequenza devono essere posizionati all'interno di un blocco xml rispettando l'ordinamento con cui è stata definita la sequenza. 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 <xmlbean>. Come mostrato di seguito, è sufficiente indicare a tale target il <fileset> 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. <target name="xmlbeans-generator" depends="init, paths-definition" description="generates the xml java beans from xsd"> <xmlbean srcgendir="${viaggio-xmlbeans.src.dir}" destfile="${viaggio-xmlbeans.libs.dir}/viaggio-xmlbeans.jar" classpathref="xmlbeans.path"> <fileset dir="${viaggio-xmlbeans.xsd.dir}"> <include name="viaggio.xsd" /> <include name="viaggio.xsdconfig" /> </fileset> </xmlbean>
</target>
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="http://www.mokabyte.it/viaggio"> <xb:namespace uri="http://www.mokabyte.it/viaggio"> <xb:package>it.mokabyte.viaggio.xmlbeans</xb:package> </xb:namespace>
</xb:config> 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 <viaggio> alla radice del documento xml. 2. L'interfaccia Viaggio contenente i metodi per accedere ad elementi ed attributi del tipo complesso <ar:viaggio>, ergo ai dati realativi al viaggio. 3. L'interfaccia Viaggiatore contenente i metodi per accedere ad elementi ed attributi del tipo complesso <ar:viaggiatore>, ergo ai dati relativi al viaggiatore. 4. L'interfaccia Tappa contenente i metodi per accedere ad elementi ed attributi del tipo complesso <ar:tappa>, ergo ai dati relativi alla tappa.
<?xml version="1.0" encoding="UTF-8"?> <viaggio xmlns:ar="http://www.mokabyte.it/viaggio" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.mokabyte.it/viaggio viaggio.xsd "> <viaggiatore> <nome>Ciccio</nome> <cognome>Macri</cognome> </viaggiatore> <dataPartenza>2007-08-03</dataPartenza> <dataRitorno>2007-08-13</dataRitorno> <tappa> <citta>Vienna</citta> </tappa> <tappa> <citta>Berlino</citta> </tappa> <tappa numeroDiGiorni="2"> <citta>Amsterdam</citta> </tappa> <tappa numeroDiGiorni="3"> <citta>Londra</citta> </tappa> </viaggio>
public ViaggioDocument readXML(String xmlPath) throws XmlException, IOException{ File xmlFile = new File(xmlPath); XmlOptions options = new XmlOptions();
options.setLoadLineNumbers(XmlOptions.LOADLINENUMBERSENDELEMENT);
ViaggioDocument viaggioDoc = ViaggioDocument.Factory.parse(xmlFile, options);
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");
public void writeXML(ViaggioDocument viaggioDoc, String xmlPath) throws IOException{
File ouputFile = new File(xmlPath);
XmlOptions options = new XmlOptions();
options.setSavePrettyPrint(); viaggioDoc.save(ouputFile,options); }
<?xml version="1.0" encoding="UTF-8"?> <ar:viaggio xsi:schemaLocation="http://www.mokabyte.it/viaggio viaggio.xsd " xmlns:ar="http://www.mokabyte.it/viaggio" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <ar:viaggiatore> <ar:nome>Ciccio</ar:nome> <ar:cognome>Macri</ar:cognome> </ar:viaggiatore> <ar:dataPartenza>2007-08-03</ar:dataPartenza> <ar:dataRitorno>2007-08-16+02:00</ar:dataRitorno> <ar:tappa> <ar:citta>Vienna</ar:citta> </ar:tappa> <ar:tappa> <ar:citta>Berlino</ar:citta> </ar:tappa> <ar:tappa numeroDiGiorni="2"> <ar:citta>Amsterdam</ar:citta> </ar:tappa> <ar:tappa numeroDiGiorni="3"> <ar:citta>Londra</ar:citta> </ar:tappa> <ar:tappa> <ar:citta>Parigi</ar:citta> </ar:tappa> </ar:viaggio>
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;
}
}
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());
}
}
