MokaByte Numero  41  - Maggio 2000
 
 JAXP 
Java API for XML Parsing
di 
Andrea  Giovannini
Una panoramica sullo stato dell'arte attuale su Java e  XML, ed un approfondimento su JAXP

In questo articolo vedremo i concetti principali di XML e quindi passeremo ad una rassegna di alcuni interessanti tool per la programmazione di applicazioni XML in Java. In particolare ci soffermeremo su JAXP, la API Java di Sun per il parsing
di documenti XML
XML è una tecnologia avviata a un utilizzo pervasivo negli ambiti di Web publishing e applicazioni business to business. Come ben sappiamo Java è il linguaggio ideale per questo tipo di applicazioni, grazie alle sue caratteristiche di portabilità ed estendibilità, ed è quindi naturale un punto di incontro delle due tecnologie.
 
 
 

XML e tecnologie correlate
In questo paragrafo vedremo gli aspetti principali di questa tecnologia e nei capitoli successivi riprenderemo i concetti e gli esempi presentati per vedere qualche esempio di programmazione Java. Si vuole precisare che questo paragrafo non vuole essere un completo tutorial di XML ma un semplice punto di partenza. 
 
 
 

Cos'è XML
XML (eXtensible Markup Language) è un meta-linguaggio per definire nuovi linguaggi basati su tag. Per questo aspetto XML è molto simile a HTML, anch'esso basato su tag, ma c'è una differenza fondamentale: XML è una notazione per definire linguaggi mentre HTML è un particolare linguaggio.
I punti chiave di XML sono 

  • separazione del contenuto dalla rappresentazione dei dati: un documento XML definisce il contenuto informativo e   non contiene alcuna informazione relativa alla formattazione dei dati; 
  • definizione di un formato standard: XML è uno standard del W3C e quindi un documento XML può essere elaborato  da qualsiasi parser o tool conforme allo standard. 


Vediamo un semplice esempio di file XML. 

<?xml version="1.0"?>
<books>
    <book>
        <author>P.K. Dick</author>
        <title>Ubik</title>
    </book>
    <book>
        <author>William Gibson</author>
       <title>Neuromante</title>
    </book>
    <book>
        <author>Franz Kafka</author>
        <title>Il processo</title>
    </book>
</books>

Il documento precedente definisce una lista di libri con i corrispondenti autori e titoli. La struttura gerarchica di un documento XML, con l'annidamento degli elementi, permette una naturale rappresentazione di una collezione di libri e delle informazioni associate ad un singolo libro. I tag utilizzati (books, book, author e title) sono completamente inventati,
hanno esclusivamente significato informativo e non contengono alcuna informazione di rendering. Ad esempio leggendo il contenuto di un elemento author possiamo risalire all'autore del libro in esame ma non sappiamo nemmeno se il documento verrà visualizzato come pagina HTML o altro.
 
 
 

Struttura di un documento XML
Così come un documento HTML deve essere redatto secondo alcune regole sintattiche ben precise perchè sia visualizzabile
da un browser anche un documento XML deve rispettare ben precise regole strutturali; in particolare 

  • ogni tag aperto deve essere chiuso; 
  • i tag non devono essere sovrapposti; 
  • i valori degli attributi devono essere racchiusi fra ""; 
  • i caratteri '<', '>' e '"' nel testo di un file XML devono essere rappresentati dai caratteri speciali '&lt;', '&gt;' e '&quot;'. 
I documenti XML che rispettano le regole precedenti vengono detti ben formati (well-formed). Oltre a poter definire
documenti ben formati è possibile specificare la particolare struttura di un file XML, ad esempio per garantire che il tag
author sia compreso all'interno del tag book. A tal proposito il W3C ha definito due modalità 
  • DTD (Document Type Definition): si tratta di un particolare documento che definisce i tag utilizzabili in un documento    XML e anche la loro struttura. Il seguente esempio mostra un DTD per il linguaggio XML di definizione di libri usato     nel documento visto in precedenza 


        <!ELEMENT books (book)+>
        <!ELEMENT book (author,title)>

        <!ELEMENT author (#PCDATA)>
        <!ELEMENT title (#PCDATA)>

Il DTD dichiara che il tag books contiene un numero non precisato di elementi book; questo a sua volta contiene i tag author e title che contengono solo caratteri. 

  • Schema: rappresentano un modo alternativo ai DTD per esprimere la struttura di un documento. Il principale vantaggio degli schema consiste nel fatto che vengono essi stessi descritti in XML! Vediamo ad esempio lo schema per la   definizione del documento XML precedente


            <xsd:schema xmlns:xsd="http://www.w3.org/1999/XMLSchema">
                    <xsd:element name="books" type="BooksType"/>

                    <xsd:complexType name="BookType">
                            <xsd:element name="book" type="BookType" minOccurs="0"
                            maxOccurs="unbounded"/>
                    </xsd:complexType>

                    <xsd:element name="book" type="BookType"/>

                    <xsd:complexType name="BookType">
                            <xsd:element name="author" type="xsd:string"/>
                            <xsd:element name="title" type="xsd:string"/>
                    </xsd:complexType>

            </xsd:schema>

Il documento precedente sarà contenuto in un file .xsd (XML Schema Definition). 
L'elemento principale è schema che a sua volta contiene altri elementi 

  • element: definisce un elemento del documento XML e le informazioni ad esso associato (numero di occorrenze,...); 
  • complexType: definisce un tipo composto, ovvero un elemento che contiene a sua volta altri elementi e avere  attributi. 
Maggiori informazioni sugli schema si possono trovare in http://www.w3.org. 
I parser XML possono essere non validanti oppure validanti. Mentre i primi verificano che il documento ricevuto in input sia ben formato e mentre i parser validanti verificano anche la struttura del documento basandosi sul corrispondente DTD o schema. Come vedremo più in dettaglio nel seguito i parser si distinguono anche in parser SAX (Simple API for XML) e DOM (Document Object Model). I parser SAX mettono a disposizione una API event-driven per la gestione dei documenti mentre i parser DOM restituiscono il documento XML con una struttura ad albero. 
 
 
 

XSL
XSL (per il momento tralascio la spiegazione dell'acronimo) è un linguaggio, sempre definito dal W3C, per trasformare un documento XML in un diverso documento XML. XSL è particolarmente potente anche perchè viene definito in XML e ha tutte le caratteristiche di estendibilità tipiche dei documenti XML. XSL è composto da 

  • XSLT: un linguaggio dichiarativo per trasformare i documenti XML; 
  • formatting objects: questa parte di XSL relativa alla formattazione dei documenti non è ancora standard e la prima implementazione disponibile è FOP (Formatting Object Processor) dell'Apache XML Group che permette di ottenere  documenti PDF. 
L'utilizzo principale di XSL consiste comunque nel trasformare un documento XML in un documento diverso (HTML, PDF, ...) che sia quindi visualizzabile con i corrispondenti viewer (browser Web). XSL sta infatti per XML Stylesheet Language. 
Nel seguito dell'articolo vedremo alcuni esempi di trasformazioni di documenti XML via XSL. E' comunque importante sottolineare ancora che XSLT permette di trasformare un documento XML in un diverso documento XML, ad esempio per eseguire alcune elaborazioni applicative; l'utilizzo di XSLT per ottenere pagine HTML è cioè solo un caso particolare anche se molto importante.
 
 
 

Tool XML per Java
Negli ultimi mesi si è avuto un grosso interesse verso XML da parte dei principali produttori di software come Microsoft, Sun, IBM e Oracle. In particolare nel mondo Java si è visto un particolare fermento per questa nuova tecnologia: per averne una prova è sufficiente visitare i siti di IBM e Oracle. 
Java è un ottimo linguaggio per programmare applicazioni XML in quanto 

  • Il binding Java del Document Object Model fornisce uno strumento standard e un punto di riferimento per gli  sviluppatori; 
  • Le caratteristiche object oriented di Java permettono di gestire in maniera naturale la struttura gerarchica dei documenti XML; 
  • XML permette la trasmissione di dati su Internet separando il contenuto dalla presentazione di questi; allo stesso modo
Java permette di scrivere applicazioni indipendenti dalla piattaforma. In questo modo è possibile combinare la portabilità dei dati e la portabilità del codice ottenendo applicazioni estremamente potenti.
Nel seguito del paragrafo vedremo una rassegna di alcuni strumenti basati su tecnologia XML per Java. 
 

Parser
 

  • XML4J: La tecnologia alla base del parser XML di IBM è stata donata all'Apache Group per lo sviluppo del parser Xerces. L'ultima versione di XML4J attualmente disponibile, la 3.0.1, è derivata dalla release 1.0.3 di Xerces. 
  • Xerces: è un parser sviluppato dal team di Apache all'interno dell'Apache XML Project. E' basato sui sorgenti di  XML4J, implementa DOM (livello 1 e 2) e SAX (versione 2) e ha un supporto preliminare per gli schema XML. E'   disponibile inoltre una versione in C++ con i wrapper Perl e COM. 
  • XP: XP è un parser non validante sviluppato da James Clark  (http://www.jclark.com)conforme alle specifiche 1.0 di   XML. Oltre a una normale API ad alto livello fornisce anche una API a basso livello er  viluppare nuovi parser. XP    è sviluppato principalmente per ottenere alte prestazioni. 


XSL
 

  • XT è sviluppato da James Clark (http://www.jclark.com). Ha varie caratteristiche interessanti come l'estendibilità attraverso codice Java e la possibilità di generare diversi file di output a fronte di un unico input. XT permette inoltre     di generare output non XML e può essere usato come servlet.
  • Xalan è un engine XSL che implementa la versione 1.0 di XSLT e la versione 1.0 di XPath. E' sviluppato dall'Apache
  • XML Group e si tratta di un prodotto derivato da LotusXSL. Per poter eseguire il parsing dei documenti XML da   trasformare e degli stylesheet XSL Xalan usa di default un parser XML con un modello a oggetti 'proprietario', il DTM     (Document Table Model). L'utilizzo di DOM può infatti penalizzare le prestazioni in quanto si deve creare un oggetto   Java per ogni elemento del documento. Il DTM utilizza invece degli array di interi. Xalan può comunque essere    configurato per utilizzare il parser Xerces. L'input può essere un file, uno stream, un documento DOM o uno stream di  eventi SAX e allo stesso modo l'output può essere un file, un documento DOM, una serie di eventi SAX o uno stream.   Xalan può essere eseguito dalla linea di comando, da un applet o anche in un servlet. Xalan è inoltre estendibile    attraverso appositi elementi e funzioni incapsulati in un namespace XML. Sono supportati diversi linguaggi di scripting    mediante l'architettura BSF (Bean Scripting Framework). Una estensione interessante di Xalan consiste nella    possibilità di generare diversi documenti di output a partire dallo stesso XML iniziale. 
  • LotusXSL fa parte dei progetti IBM alphaWorks. Da novembre 1999 il progetto è stato passato all'Apache Group per  lo sviluppo di Xalan. La release di LotusXSL include una serie di classi wrapper che permettono di usare la API   LotusXSL pre-Xalan che a sua volta richiama la API Xalan. Le nuove funzionalità fanno comunuqe parte della API   Xalan. 


Web publishing

Cocoon è un framework per il web-publishing completamente basato su Java e le tecnologie XML ( comprendendo quindi XSL e DOM). L'idea di base di Cocoon è quella di separare le fasi dello sviluppo di contenuti Web: 

  • creazione del documento XML: nella vision Cocoon il documento XML viene prodotto in genere da utenti umani che conoscono dei contenuti del documenti ma ai quali non si richiede alcuna conoscenza relativa all'elaborazione del contenuto; 
  • elaborazione del documento XML: una delle peculiarità di Cocoon consiste nella separazione fra creazione del documento XML e la sua elaborazione applicativa (non si tratta quindi di rendering). A tal proposito si utilizza il termine logicsheet e Cocoon propone XSP (eXtensible Server Pages), un linguaggio XML per descrivere la logica applicativa per elaborare i documenti XML; 
  • rendering XSL: per la visualizzazione del documento si applica uno stylesheet XSL e si formatta il documento nel formato richiesto (XML, HTML, PDF, ...). 


L'architettura di Cocoon è fortemente basata sulle caratteristiche di XSLT. Il framework espone una API attraverso XML e un'interfaccia più sofisticata in Java. 
Cocoon è inoltre in grado di distinguere il browser che ha originato la richiesta e determinare quale stylesheet applicare al documento richiesto in modo da ottenere un output adeguato per le capacità del browser. 
Questa breve rassegna si è limitata ai principali strumenti disponibili per sviluppare applicazioni Java basate su XML.
Oltre a questi è possibile trovarne altri come ad esempio, nel sito IBM, vari tool visuali per l'editing dei documenti XML e XSL. 

JAXP: Java API for XML Parsing
JAXP (API chiamata precedentemente Project X) vuole fornire al programmatore i mattoni di base per realizzare applicazioni XML. Più che di applicazioni si può parlare di un framework di sviluppo basato su servizi e applicazioni. Le funzionalità di JAXP rappresentano il cosiddetto core per la realizzazione di applicazioni XML in Java. Questo nucleo di base include 

  • La possibilità di utilizzare diversi parser (pluggability);
  • API per il parsing (con eventuale validazione) dei documenti;
  • DOM API per poter gestire un documento XML come una struttura ad albero;


Le API precedentemente citate si triovano nel package com.sun.xml.*. Sulla base degli elementi forniti da JAXP è possibile costruire dei servizi, ovvero moduli che forniscono funzionalità troppo specializzate per far parte della core API. Ad esempio un modulo per l'elaborazione dei documenti in XSL potrebbe essere utile in vari contesti ma non in tutti. 
Utilizzando la core API ed eventualmente servizi addizionali si possono sviluppare applicazioni complete basate su XML
come Web-publishing e transazioni per l'e-commerce business-to-business. 
Veniamo ora ad analizzare la API che a un primo livello si può suddividere in API per il parsing, definita nel package javax.xml.parsers, e API per il DOM. Il DOM, Document Object Model, è una recommendation del W3C che definisce una API per l'elaborazione dei documenti XML; tale API è definita nel package org.w3c.dom.
La API per il parsing fornisce le classi e le interfacce per l'elaborazione dei documenti XML. Vengono supportati i parser DOM e SAX in modo pluggable, ovvero è possibile utilizzare parser di terzi all'interno delle proprie applicazioni continuando ad utilizzare la API JAXP. Ad esempio la classe DocumentBuilder permette di ottenere un documento DOM a partire da un documento XML. E' importante osservare che un oggetto di classe DocumentBuilder può fare riferimento a diverse implementazioni.

Il codice seguente mostra come eseguire il parsing del documento XML e ottenere un documento DOM, rappresentato dall'interfaccia Document. 

DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder db = dbf.newDocumentBuilder();
InputStream is = new FileInputStream("test.xml");
Document doc = db.parse(is);
doc.getDocumentElement().normalize();

A questo punto è possibile navigare la struttura ad albero del documento: 

Node book = doc.getDocumentElement();
NodeList book_list = doc.getElementsByTagName("book"),
book_items;

for(int i=0; i<book_list.getLength(); i++) {
  book_items = book_list.item(i).getChildNodes();
  for(int j=0; j<book_items.getLength(); j++) {
    System.out.println(
      book_items.item(j).getFirstChild().getNodeValue());
  }
}

E' importante considerare che il codice precedente è completamente indipendente dalla particolare implementazione del
parser DOM utilizzato. 
JAXP fornisce anche il supporto per la API SAX attraverso la classe SAXParser. SAX (Simple API for XML) è un'interfaccia event-driven standard definita dal W3C per l'elaborazione dei documenti XML. Il supporto per la API SAX si trova nel package org.xml.sax mentre il package org.xml.sax.helpers fornisce alcune classi accessorie. A differenza di DOM, che crea la struttura ad albero del documento residente in memoria, i parser SAX generano eventi che vengono intercettati da un apposito handler fornito dallo sviluppatore. Più precisamente il parser SAX legge il documento XML e genera eventi corrispondenti alla struttura del documento. Lo sviluppatore deve definire un handler, che implementa l'interfaccia org.xml.sax.DocumentHandler, per definire i metodi callback che vengono invocati in corrispondenza degli eventi. Per semplificare il compito dei programmatori viene fornita la classe org.xml.sax.HandlerBase che implementa tutti
i metodi di DocumentHandler come no-operation; i metodi sono cioè vuoti. In questo modo per definire un proprio handler è sufficiente estendere HandlerBase e ridefinire solo i metodi necessari. 
Vediamo ora come istanziare il parser SAX ed eseguire il parsing di un documento. 

SAXParserFactory spf = SAXParserFactory.newInstance();
spf.setValidating(true);
SAXParser sp = spf.newSAXParser();
Parser parser = sp.getParser();

parser.setDocumentHandler( new MyDocumentHandler() );
parser.setErrorHandler( new MyErrorHandler() );

String file_name = "file:" + new File("test.xml").getAbsolutePath();
parser.parse( new InputSource(file_name) );

L'input del parser è un'istanza della classe org.xml.sax.InputSource che incapsula appunto la sorgente dati di un documento
XML in questo caso specificata attraverso un system ID, cioè un URI a un file. La classe MyDocumentHandler definisce un
handler per il tipo di documento in esame. Supponiamo di voler contare il numero di libri di P.K. Dick citati nel documento

public class MyDocumentHandler extends HandlerBase {
    private int counter;
    private boolean is_author;

   public MyDocumentHandler() {
        super();
        counter = 0;
        is_author = false;
    }

    public void characters(char[] ch, int start, int length) throws SAXException {
        if (is_author) {
            String data = new String(ch, start, length);
            if (data.equalsIgnoreCase("P.K. Dick")) {
                counter++;
            }
         }
   }

    public void startElement(String name, AttributeList attributes) throws SAXException {
        if (name.equals("author")) {
            is_author = true;
        }
    }

    public void endElement(String name) throws SAXException {
        if (name.equals("author")) {
            is_author = false;
        }
    }

    public void endDocument() {
        System.out.println("Found " + counter + " P.K. Dick's books");
    }
}

Analogamente anche per la gestione degli errori si prevede un apposito handler che implementi l'interfaccia org.xml.sax.ErrorHandler; tale interfaccia definisce i seguenti metodi 

    warning(): segnalazione di un warning; 
    error(): errore recuperabile; 
    fatalError(): errore non recuperabile; 

La classe HandlerBase implementa comunque anche i metodi di ErrorHandler e può essere estesa per definire un gestore
di errori custom. Il parser può quindi essere configurato per segnalare gli errori non con eccezioni al programma client ma
attraverso l'interfaccia ErrorHandler. La nostra implementazione di esempio ridefinisce i metodi error e warning. 

public class MyErrorHandler extends HandlerBase {
   public void error(SAXParseException e) throw SAXException {
  throw e;
}

public void warning(SAXParseException e) throw SAXException{
   System.out.println(e.getMessage());

}
 
 
 

Un esempio completo
Vediamo ora un esempio completo di utilizzo di XML in una ipotetica applicazione per la gestione di libri. Dovremo innanzitutto realizzare degli oggetti applicativi che incapsulano le informazioni relative a un libro (nel nostro semplice caso ci limitiamo ad autore e titolo). Un requisito della nostra applicazione è che tali oggetti possano essere istanziati anche a partire da un file XML, o meglio da un frammento di file XML visto che un file contiene informazioni relative a più libri.
Segue ora il codice della classe Book che implementa i requisiti precedenti 

import org.w3c.dom.Element;
public class Book {
    private String author;
    private String title;

    // Default constructor.
    public Book() {}

    // Build a Book object with the specified author and title.
    public Book(String author, String title) {
        this.author = author;
        this.title = title;
    }

    // Build a Book object from a DOM element.
    public Book(Element element) {
       author = element.getElementsByTagName
         ("author").item(0).getFirstChild().getNodeValue();
       title = element.getElementsByTagName
         ("title").item(0).getFirstChild().getNodeValue();
    }
        // get methods
    public String getAuthor() {
        return author;
    }

    public String getTitle() {
        return title;
    }

    // set methods
    public void setAuthor(String author) {
        this.author = author;
    }

    public void setTitle(String title) {
        this.title = title;
    }
}

Soffermiamoci sul costruttore Book(Element element). Esso riceve in ingresso un elemento DOM rappresentato dall'interfaccia Element. Si usa quindi il metodo getElementsByTagName() in modo da reperire una lista di nodi il cui tag
abbia il nome specificato. Il metodo ritorna un'implementazione dell'interfaccia NodeList che rappresenta una lista di nodi del documento. In questo caso contiene un solo elemento; si accede quindi all'informazione associata al nodo mediante la
catena di chiamate .item(0).getFirstChild().getNodeValue(). La classe DOMTest mostra come caricare una collezione di oggetti Book con le informazioni contenute nel file XML specificato 

import java.io.*;
import java.util.*;
import javax.xml.parsers.*;
import org.w3c.dom.*;
import org.xml.sax.*;

import Book;

public class DOMTest {

    public static void main(String args[]) {

        if (args.length != 1) {
            System.err.println("Usage: java DOMTest xmlfilename");
            System.exit(1);
        }

        try {
            DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
            DocumentBuilder db = dbf.newDocumentBuilder();

            Document doc = db.parse(new File(args[0]));
            NodeList nl = doc.getElementsByTagName("book");
            Element e;
            Collection c = new Vector();

            // fill collection with book objects
            for (int i=0; i<nl.getLength(); i++) {
                e = (Element)nl.item(i);
                c.add( new Book(e) );
            }

            // print out collection content
            Iterator    i = c.iterator();
            Book        book;
            while (i.hasNext()) {
                book = (Book)i.next();
                System.out.println("Author: " + book.getAuthor() +
                        " - title: " + book.getTitle());
            }

        }
        catch (IOException ioe) {
            System.err.println("Input/Output error: " + ioe.getMessage());
            System.exit(1);
        }
        catch (SAXParseException spe) {
            System.err.println("Parsing exception for entity " + spe.getPublicId() +
                " at line: " + spe.getLineNumber() +
                " column: " + spe.getColumnNumber());
            System.exit(1);
        }
        catch (SAXException se) {
            System.err.println("General SAX exception: " + se.getMessage());
            System.exit(1);
        }
        catch (ParserConfigurationException pce) {
            System.err.println("General SAX exception: " + pce.getMessage());
            System.exit(1);
        }
        catch (FactoryConfigurationError fce) {
            System.err.println("Configuration error: " + fce.getMessage());
            System.exit(1);
        }
    }
}

Innanzitutto si crea un parser XML per ottenere un documento DOM a partire dal file XML fornito come argomento alla linea di comando. Si ottiene quindi la lista di nodi "book" contenuti nel documento e per ognuno di essi si crea un oggetto
Book da inserire in una collezione di oggetti. Al termine di questa elaborazione si ottiene una collezione pronta per eventuali elaborazioni. Nel nostro esempio si itera  semplicemente la collezione per visualizzarne il contenuto. La gestione degli errori mostra alcuni aspetti interessanti: in particolare nel caso si verifichi un errore  di parsing questo viene segnalato dall'eccezione SAXParseException che permette di ottenere precise informazioni relative alla posizione in cui si è verificato l'errore nel file XML. 

Conclusioni
In questo articolo abbiamo visto le potenzialità delle applicazioni XML sviluppate con Java. In particolare abbiamo esaminato JAXP, che rappresenta il contributo Sun sul fronte XML, ma abbiamo anche visto che vi sono diversi e interessanti tool di terze parti per sviluppare applicazioni XML con Java. Ci sarebbero molti altri aspetti interessanti da esaminare, come ad esempio l'utilizzo di XML con specifiche tecnologie Java come EJB e JMS. Per ulteriori approfondimenti si possono trovare alcuni link nella sezione Riferimenti.
 

Riferimenti
 

  1. http://java.sun.com/xml: sito della Sun dedicato a Java e XML.
  2. http://www.w3.org: sito del World Wide Web Consortium.
  3. http://www.ibm.com/xml: fornisce vari articoli, tool e informazioni su XML con particolare riferimento a Java.
  4. http://xml.apache.org: sito dell'XML Apache Group.
  5. http://www.xbeans.org: sito del progetto Open Source XBeans basato sull'utilizzo di Java Bean per l'elaborazione di documenti XML in applicazioni distribuite.
 
Chi volesse mettersi in contatto con la redazione può farlo scrivendo a mokainfo@mokabyte.it