JAVA DOM PARSING

Il parsing di documenti XML è un’operazione molto diffusa e utile. In Java esistono sostanzialmente due approcci: DOM e SAX. Mentre SAX (leggi qui) si basa sulla generazione di eventi quando si verificano determinate situazioni (es. inizio del documento, inizio di un elemento, fine di un elemento...)  e non mantiene in memoria il documento,  con DOM tutto l'albero del documento viene caricato in memoria e possiamo lavorare su di esso come meglio preferiamo. Appare subito evidente che SAX utilizza al meglio le risorse (DOM soffre con documenti XML molto grandi), ma non consente di navigare il documento al contrario né di modificare o creare documenti XML.
Con DOM,  ogni elemento del documento XML diventa un nodo dell'albero rappresentato da una classe che implementa l'interfaccia org.w3c.dom.Node. Questa interfaccia rappresenta un singolo nodo nell’albero del documento e fornisce metodi per esplorare o modificare l'albero e per conoscere le proprietà di ogni nodo.
 
I punti principali da seguire per elaborare un documento XML con DOM sono:

  •  Importare  i package fondamentali:
        import javax.xml.parsers.*;
        import org.w3c.dom.*;
        import org.xml.sax.*;
  • Occorre poi istanziare un oggetto DocumentBuilderFactory e impostare le caratteristiche del parser con una serie di metodi setters, che tra le altre cose consentono di ignorare i commenti, di impostare la non validazione del documento XML e così via.
  • Si crea poi l’oggetto DocumentBuilder, che consente di effettuare il parsing di un documento XML.
  • Si effettua il parsing del documento utilizzando il metodo parse() in una delle seguenti modalità:
       void parse(File f)
       void parse(String uri)
  • Il parsing restituisce un oggetto di tipo Document, che rappresenta l’albero del documento XML. L’albero può poi essere interrogato con i metodi messi a disposizione dalle varie classi.

Ad esempio, l’interfaccia Document (sotto-intefaccia di Node)  ci mette a disposizione i seguenti metodi:

  • Element getDocumentElement()
    restituisce un riferimento all'elemento radice del documento
  • Element getElementById(String elemento)
    restituisce un riferimento all'elemento con ID specificato
  • Element getElementsByTagName(String tagname)
    restituisce una NodeList degli elementi con il tag specificato
  • DocumentType getDocType()
    restituisce un riferimento alla DTD associata al documento

L'interfaccia Node, invece, ci offre:

  • NamedNodeMap getAttributes()
    restituisce un riferimento a una NamedNodeMap degli attributi del nodo se è un elemento o null altrimenti
  • NodeList getChildNodes()
    restituisce un riferimento a una NodeList dei figli del nodo;
  • Node getFirstChild()
    restituisce un riferimento al primo figlio del nodo.
  • Node getLastChild()
    restituisce un riferimento all'ultimo figlio del nodo
  • Node getNextSibling()
    restituisce un riferimento al nodo successivo di pari livello
  • String getNodeName()
    restituisce il nome del nodo
  • String getNodeValue()
    valore del nodo per i nodi di testo, CDATA, commento e attributo;
  • Document getOwnerDocument()
    riferimento all'oggetto Document che contiene il nodo
  • Node getParentNode()
    restituisce un riferimento al nodo di livello superiore
  • Node getPreviousSibling()
    restituisce un riferimento al nodo precedente di pari livello
  • boolean hasAttributes()
    restituisce true se il nodo ha attributi
  • boolean hasChildNodes()
    restituisce true se il nodo ha nodi figli
  • short getNodeType()
    restituisce il tipo del nodo;

I tipi di nodo sono costanti dell'interfaccia Node:

Node.ATTRIBUTE_NODE
Node.ELEMENT_NODE
Node.COMMENT_NODE
Node.DOCUMENT_FRAGMENT_NODE
Node.DOCUMENT_NODE
Node.DOCUMENT_TYPE_NODE
Node.CDATA_SECTION_NODE
Node.TEXT_NODE
Node.ENTITY_NODE
Node.ENTITY_REFERENCE_NODE
Node.NOTATION_NODE
Node.PROCESSING_INSTRUCTION_NODE

Per quanto riguarda l’interfaccia NodeList, questa offre I metodi:

  • int getLength()
            restituisce il numero di nodi della lista
  • Node item(int index)
            riferimento al nodo della lista alla posizione specificata

Il codice seguente mostra come è possibile analizzare con DOM un documento XML (il cui percorso è passato come parametro della riga di comando) e stamparne le informazioni principali.

public class DocumentDomCleanParser{
 
       //main
       public static void main(String[] args) {
             String filename = args[1];
             DocumentDomCleanParser ddp = new DocumentDomCleanParser();
             DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
 
             try {
                    DocumentBuilder builder = dbf.newDocumentBuilder();
                    File xmlFile = new File(filename);
                    Document document = builder.parse(xmlFile);
                    ddp.printNodeInfo(document);
             } catch (SAXException sxe) {
                    Exception  x = sxe;
                    if (sxe.getException() != null)
                           x = sxe.getException();
                    x.printStackTrace();
             } catch (ParserConfigurationException pce) {
                    pce.printStackTrace();
             } catch (IOException ioe) {
                    ioe.printStackTrace();
             }
 
       }
 
 
       /**
        * Stampa le info sui nodi, in modo ricorsivo
        * @param currentNode il nodo corrente
        */

       public void printNodeInfo(Node currentNode) {
             short sNodeType = currentNode.getNodeType();
             //Se è di tipo Element ricavo le informazioni e le stampo
             if (sNodeType == Node.ELEMENT_NODE) {
                    String sNodeName = currentNode.getNodeName();
                    String sNodeValue = searchTextInElement(currentNode);
                    NamedNodeMap nnmAttributes = currentNode.getAttributes();
                    System.out.println("Elemento: " + sNodeName);
                    System.out.println("Attributi: " +
                                 printAttributes(nnmAttributes));
                    if (!sNodeValue.trim().equalsIgnoreCase("")) {
                           System.out.println("Contenuto: " + sNodeValue);
                    }
                    System.out.print("\n");
             }
             int iChildNumber = currentNode.getChildNodes().getLength();
             //Se non si tratta di una foglia continua l'esplorazione
             if (currentNode.hasChildNodes()) {
                    NodeList nlChilds = currentNode.getChildNodes();
                    for (int iChild = 0; iChild < iChildNumber; iChild++) {
                           printNodeInfo(nlChilds.item(iChild));
                    }
             }
       }
 
       /*
        * Search the content for a given Node
        */

       private static String searchTextInElement(Node elementNode) {
             String sText = "";
             if (elementNode.hasChildNodes()) {
                    //Il child node di tipo testo è il primo
                    Node nTextChild = elementNode.getChildNodes().item(0);
                    sText = nTextChild.getNodeValue();
             }
             return sText;
       }
 
       private static String printAttributes(NamedNodeMap nnm) {
             String sAttrList = new String();
             if (nnm != null && nnm.getLength() > 0) {
                    for (int iAttr=0; iAttr < nnm.getLength(); iAttr++) {
                           sAttrList += nnm.item(iAttr).getNodeName();
                           sAttrList += "=";
                           sAttrList += nnm.item(iAttr).getNodeValue();
                           sAttrList += "; ";
                    }
                    return sAttrList;
             }
             else {
                    return "assenti";
             }
       }
}