Linguaggi e tecnologie per il Web
Corso di Laurea in Ingegneria Informatica e Automatica
Sapienza Università di Roma
a.a. 2015/2016
XML
Parte 3:
Accesso diretto a documenti XML (DOM e SAX)
Luigi Dragone, Riccardo Rosati
Il DOM (Document Object Model) è un modello ad oggetti dei dati contenuti in un documento XML
L’API del DOM è un’interfaccia di programmazione applicativa che consente di manipolare opportunamente tali strutture dati
Il modello di un documento è assimilabile ad una struttura ad albero
Un nodo dell’albero può essere un elemento, un attributo, una sezione #PCDATA o una sezione CDATA del documento
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 nodo è la struttura fondamentale del DOM, ogni altra struttura è ottenuta come specializzazione dell’interfaccia Node supporta essenzialmente 3 tipi di operazioni:
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
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
Il nodo è la struttura fondamentale del DOM, ogni altra struttura è ottenuta come specializzazione dell’interfaccia Node
Estende l’interfaccia Node per rappresentare le caratteristiche tipiche di un documento
Estende l’interfaccia Node per rappresentare le caratteristiche tipiche di un elemento
Estende l’interfaccia Node per rappresentare le caratteristiche tipiche di un attributo
Estende l’interfaccia Node per rappresentare le caratteristiche tipiche di una sezione #PCDATA
Questa interfaccia rappresenta una mappa di nodi indicizzata sul nome e consente di accedere ad un nodo dell’insieme attraverso il nome
Questa interfaccia rappresenta una lista di nodi
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();
Questa classe permette di costruire un modello di documento (istanza del DOM)
La costruzione può avvenire in due modi
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
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);
…
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);
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)
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)
Serializzazione del documento doc sul file doc.xml
Document doc = …;
…
XMLSerializer ser = new XMLSerializer();
ser.setCharOutputStream(new FileWriter("doc.xml"));
ser.serialize(doc);
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();
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 }
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 è un’interfaccia di programmazione (API) concepita per la gestione agevole ed efficiente di documenti XML
Proposta dalla mailing-list XML-Dev, quindi recepita ed implementata da diversi produttori (Microsoft, Oracle, IBM, Sun) e dalla comunità OpenSource
SAX è basata sul concetto di evento associato al processo di parsing di un documento
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:
L’utilizzo di SAX prevede l’interazione di tre oggetti distinti
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
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
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
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()
Questa classe consente di accedere agli attributi di un elemento
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
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
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");
}
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
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)
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.