Linguaggi per il Web
Corso di Laurea in Ingegneria Informatica e Automatica
Corso di Laurea in Ingegneria dei Sistemi Informatici
Sapienza Università di Roma
a.a. 2013/2014

XML

Parte 3:
Accesso diretto a documenti XML (DOM e SAX)

Luigi Dragone, Riccardo Rosati


Accesso diretto a documenti XML (DOM e SAX)

Accesso diretto a documenti XML si ottiene tramite
  1. DOM (Document Object Model)
  2. SAX (Simple API for XML)

DOM: Document Object Model


DOM API

L’API DOM è definito come un insieme di interfacce CORBA (IDL)

L’API è costituita da una serie di interfacce per gli elementi del DOM (elementi, attributi, entità) e per oggetti di “appoggio” (liste di nodi e mappe di attributi)

Lo standard è definito dal W3C ed esistono implementazioni in diversi linguaggi (C++, Java, VisualBasic, Delphi)

La libreria JAXP (Java API for XML Processing) di Java permette di utilizzare le API del DOM

(JAXP:DOM=JDBC:RDBMS)


Il nucleo DOM


Interfaccia Node

Il nodo è la struttura fondamentale del DOM, ogni altra struttura è ottenuta come specializzazione dell’interfaccia Node supporta essenzialmente 3 tipi di operazioni:

  1. informazioni su un nodo
  2. attraversamento dell'albero
  3. modifica dei nodi

Estensioni dell'Interfaccia Node

Interfaccia Document: Estende Node per rappresentare (la radice di) un documento. Un Node non può essere creato se non viene creato un Document

Interfaccia Element: Estende Node per rappresentare un elemento

Interfaccia Attr: Estende Node per rappresentare un attributo

Interfaccia Text:Estende Node per rappresentare una sezione #PCDATA


Interfacce di collegamento

Interfaccia NamedNodeMap: rappresenta una mappa di nodi indicizzata sul nome e consente di accedere ad un nodo dell’insieme  attraverso il nome

Interfaccia NodeList: rappresenta una lista di nodi


Interfaccia Node

Il nodo è la struttura fondamentale del DOM, ogni altra struttura è ottenuta come specializzazione dell’interfaccia Node


Interfaccia Document

Estende l’interfaccia Node per rappresentare le caratteristiche tipiche di un documento


Interfaccia Element

Estende l’interfaccia Node per rappresentare le caratteristiche tipiche di un elemento


Interfaccia Attr

Estende l’interfaccia Node per rappresentare le caratteristiche tipiche di un attributo


Interfaccia Text

Estende l’interfaccia Node per rappresentare le caratteristiche tipiche di una sezione #PCDATA


Interfaccia NamedNodeMap

Questa interfaccia rappresenta una mappa di nodi indicizzata sul nome e consente di accedere ad un nodo dell’insieme  attraverso il nome


Interfaccia NodeList

Questa interfaccia rappresenta una lista di nodi


Costruzione di un Document

Occorre utilizzare le classi DocumentBuilderFactory e DocumentBuilder del package javax.xml.parser

1) Si deve istanziare un DocumentBuilderFactory

DocumentBuilderFactory docBuilderFact =
 DocumentBuilderFactory.newIstance();

2) Quindi un DocumentBuilder

DocumentBuilder docBuilder =
 docBuilderFact.newDocumentBuilder();


Classe DocumentBuilder

Questa classe permette di costruire un modello di documento (istanza del DOM)

La costruzione può avvenire in due modi

  1. Mediante parsing di un documento già esistente
  2. Mediante costruzione di un documento vuoto ex-novo

Per costruire un documento vuoto si utilizza il metodo newDocument() che restituisce un oggetto di tipo Document

Per costruire un documento mediante parsing si possono adoperare un insieme di metodi tra loro equivalenti, che si differenziano solo per la modalità di accesso al documento


Esempio

Parsing di un documento contenuto in una stringa s

String s="<A><B/></A>";
StringReader sr = new StringReader(s);
InputSource is = new InputSource(sr);
Document doc = docBuilder.parse(is);


Esempio

Costruzione del documento

<A a="v">
   <B/>
   <C>text</C>
</A>

Creazione del documento vuoto

    Document doc = docBuilder.newDocument();

Creazione dell’elemento A

    Element elemA = doc.createElement("A");

Valorizzazione dell’attributo a dell’elemento A

    elemA.setAttribute("a","v");

Aggiunta dell’elemento A al documento come root-element

    doc.appendChild(elemA);

Creazione dell’elemento B

    Element elemB = doc.createElement("B");

Aggiunta dell’elemento B all’elemento A

    elemA.appendChild(elemB);

Creazione dell’elemento C

    Element elemC = doc.createElement("C");

Creazione e valorizzazione del nodo di testo

    Text text = doc.createText("text");

Aggiunta del nodo di testo all’elemento C

    elemC.appendChild(text);

Aggiunta dell’elemento C all’elemento A

    elemA.appendChild(elemC);


Serializzazione

La serializzazione è l’operazione inversa del parsing: dato un DOM viene prodotta una rappresentazione XML del medesimo documento

Al momento non esistono API standardizzate per la serializzazione (a differenza del parsing)

Riferimento: la serializzazione in Xerces (parser XML di Apache)


Classe XMLSerializer

La classe XMLSerializer è inclusa nel package org.apache.xml.serializer

Dopo aver creato un’istanza di questa classe è necessario impostare il canale di uscita con il metodo setOutputCharStream(Writer outStream)

Per serializzare un DOM è sufficiente invocare il metodo serialize(Element elem)


Esempio

Serializzazione del documento doc sul file doc.xml

Document doc = …;

XMLSerializer ser = new XMLSerializer();
ser.setCharOutputStream(new FileWriter("doc.xml"));
ser.serialize(doc);


Esempio

Serializzazione del documento doc nella string strDoc

Document doc = …;

StringWriter os = new StringWriter();
XMLSerializer ser = new XMLSerializer();
ser.setCharOutputStream(os);
ser.serialize(doc);
String strDoc = os.toString();


Esempio

Lettura dell'albero di un documento e stampa degli elementi e dei loro attributi:

import javax.xml.parsers.*;
import org.w3c.dom.*;
import org.xml.sax.*;
import java.io.*;
 
public class AnalisiDOM {
  private Document d;
  private Element r;
 
  public AnalisiDOM(String file) {
    try {
      DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
      DocumentBuilder domParser = dbf.newDocumentBuilder();
 
      d = domParser.parse(new File(file));
      r = d.getDocumentElement();
      esaminaNodo(r);
    }
    catch(SAXParseException e) {
      System.out.println("Errore di parsing: "+ e.getMessage());
      System.exit(1);
    }
    catch(FileNotFoundException e) {
      System.out.println("File "+ file + " non trovato");
      System.exit(1);
    }
    catch(Exception e) {
      e.printStackTrace();
    }
  }// AnalisiDOM
 
  public void esaminaNodo(Node nodo) {
    switch(nodo.getNodeType()) {
      case Node.ELEMENT_NODE:
        System.out.println("Elemento: " + nodo.getNodeName());
        NamedNodeMap attributi = nodo.getAttributes();
        if(attributi.getLength() > 0) {
          System.out.println("\tAttributi: ");
          for(int i=0; i<attributi.getLength(); i++) {
            Attr attributo = (Attr) attributi.item(i);
            System.out.print(attributo.getNodeName() + "=" + attributo.getNodeValue() + " ");
          }
          System.out.println();
        }
        esaminaFigli(nodo.getChildNodes());
        System.out.println("Fine elemento: " + nodo.getNodeName());
        break;
    case Node.CDATA_SECTION_NODE:
    case Node.TEXT_NODE:
      Text testo = (Text)nodo;
      if(!testo.getNodeValue().trim().equals(""))
        System.out.println("\tTesto; " + testo.getNodeValue());
      break;
    }
  }// esaminaNodo
 
  public void esaminaFigli(NodeList figli) {
    if(figli.getLength() > 0)
      for(int i=0; i<figli.getLength(); i++)
        esaminaNodo(figli.item(i));
  }// esaminaFigli
}

Vantaggi del DOM


Svantaggi del DOM

Il DOM di un documento generico è generalmente 10 volte più “grande” del documento stesso. Pertanto non è consigliabile adoperare il DOM per manipolare documenti di grandi dimensioni


SAX: Simple API for XML


Eventi

La specifica di un componente basato su SAX riguarda le azioni da compiere al verificarsi di un determinato evento in fase di parsing di un documento:


Architettura

L’utilizzo di SAX prevede l’interazione di tre oggetti distinti


Realizzazione

Il content handler è un componente che implementa una particolare interfaccia che espone dei metodi associati ai diversi eventi

Al verificarsi di un evento il parser invoca il metodo corrispondente dell’oggetto

Il compito dell’applicazione è quello di

  1. istanziare opportunamente il parser
  2. istanziare i content handler (possono essere molteplici) e registrarli presso il parser
  3. avviare il parsing

SAX per Java

Esistono diverse implementazioni di SAX in diversi linguaggi di programmazione (C++, Java, VisualBasic)

Riferimento: versione 2.0 per Java

L'interfaccia al parser è standard come per DOM, le classi che implementano il parser
devono essere presenti nel CLASSPATH, in questo caso la libreria javax.xml.parsers


Creazione del parser

Si deve istanziare un oggetto della classe SAXParserFactory

    SAXParserFactory parserFact =
       SAXParserFactory.newInstance();

Quindi da quest’ultimo si ottiene un’istanza di parser SAX

    SAXParser parser =
       parserFact.newSAXParser();

Non è necessario indicare quale implementazione utilizzare, pertanto il codice risulta portabile su diverse piattaforme con diverse implementazioni del parser


Interfaccia ContentHandler

E’ necessario definire una classe che implementi l’interfaccia ContentHandler del package org.xml.sax

Questa interfaccia espone un metodo per ciascun evento che può essere generato dal parser nel corso dell’analisi del documento

Inizio e fine del documento
    void startDocument()/void endDocument()
 

Sezione #PCDATA, in ch è contenuto il testo sotto forma di array di caratteri (può essere convertito in stringa con new String(ch, start, length))
    void characters(char[] ch, int start, int length) 

Inizio di un elemento di nome localName appartenente al namespace namespaceURI con attributi atts
    void startElement(String namespaceURI,   
     String localName, String qName, Attributes atts)

Fine di un elemento (anche se EMPTY)
    void endElement()


Classe Attributes

Questa classe consente di accedere agli attributi di un elemento


Classe DefaultHandler

L’interfaccia ContentHandler possiede molti altri metodi, tuttavia nella maggior parte dei casi è interessante implementarne solo alcuni

La classe DefaultHandler implementa l’interfaccia ContentHandler con metodi vuoti (no-action)

Pertanto per implementare un gestore del contenuto del documento si deriva una classe da DefaultHandler e si ridefiniscono i soli metodi richiesti


Esempio

Si vuole tracciare il parsing di un documento XML, ovvero segnare su un dispositivo di log (ad esempio la console) il riconoscimento delle diverse sezioni di un documento XML

In particolare si è interessati ad individuare l’inizio e la fine di un documento, di un elemento e le sezioni #PCDATA

A tal fine è necessario implementare un ContentHandler estendendo il DefaultHandler


Esempio

import java.io.*;
import javax.xml.parsers.*;
import org.xml.sax.*;
import org.xml.sax.helpers.*;
public class TraceHandler extends DefaultHandler {
 public void startDocument() {
  System.out.println("Inizio documento");
 }
 public void endDocument() {
  System.out.println("Fine documento");
 }
 public void characters(char[] ch, int start, int len) {
  System.out.println(new String(ch,start,len));
 }
 public void startElement(String namespaceURI,String localName, String qName, Attributes atts) {
  System.out.println("Inizio elemento "+qName);
  for(int i=0; i<atts.getLength(); i++)
    System.out.println("Attributo“+
         atts.getQName(i)+
         " valore "+atts.getValue(i));
 }
 public void endElement() {
  System.out.println("Fine elemento");
 }


Esempio (continua)

Definiamo, inoltre, un metodo &main che invoca il parser sul file specificato come primo argomento della linea di comando

 public static void main(String[] args) {
  try {
    SAXParserFactory parserFactory = 
       SAXParserFactory.newInstance();
    SAXParser parser = 
       parserFactory.newSAXParser();
    parser.parse(new File(args[0]),new TraceHandler());
      }
      catch(Exception e) {
          e.printStackTrace();
      }
 }
}

Per poter effettuare il parsing del file myFile.xml è sufficiente

java TraceHandler myFile.xml


Confronto tra SAX e DOM


Confronto tra SAX e DOM

Vantaggi di SAX:


Confronto tra SAX e DOM

Svantaggi di SAX:


Oltre DOM e SAX: StAX

diverse proposte per superare le limitazioni di DOM e SAX

StAX: Streaming API for XML

StAX è una streaming XML API come SAX, ma è di tipo pull e non push

pull API = l'applicazione che usa API StAX comanda il parser attraverso richieste di lettura dell'input

(aggiunta in Java JDK 6)


Riferimenti

DOM W3C Working Group: http://www.w3c.org/DOM/

Document Object Model (DOM) Level 2 Core Specification Version 1.0 W3C Recommendation 13 November, 2000

Specifiche SAX: http://www.saxproject.org/

Tutorial SAX: http://www.onjava.com/pub/a/onjava/2002/06/26/xml.html

(si veda anche http://www.xml.com/)

Tutorial on Java API for XML Processing, Sun® Microsystems

An introduction to StAX, O'Reilly Media, Inc.