Table des matières

TP XML et Java

API DOM

L'API DOM Java offre des fonctionnalités similaires à celle de l'API DOM Javascript.

Afin de permettre plusieurs implémentations de cette API, la bibliothèque Java défini essentiellement des interfaces et des classes abstraites. On utilise ainsi rarement les constructeurs pour fabriquer de nouveau objets de cette API, mais plutôt des méthodes qui vont se charger d'appeler les bons constructeurs.

Une classe de base de cette API est la classe DocumentBuilder. Les objets de cette classe peuvent être fabriqués via des objets de la méthode DocumentBuilderFactory.newDocumentBuilder(). Les objets DocumentBuilder comportent les méthodes newDocument() et parse(…).

  • La méthode newDocument() permet de construire un document XML vide en mémoire.
  • La méthode parse(…) permet de lire une document XML donné sous forme de fichier ou de flux et d'en construire une représentation mémoire.

La structure d'un document XML est représentée par un ensemble d'objets représentant les différents noeuds de l'arbre XML. Il existe une interface par type de noeud, toutes ces interfaces dérivant directement ou indirectement de l'interface Node. Il peut être intéressant de regarder les méthodes appendChild(), getAttributes(), getChildNodes(), getNodeName(), getNodeType(), getNodeValue(), getTextContent(). La méthode getNodeType() permet de déterminer le type de noeud. Il est ensuite possible d'utiliser un cast pour voir l'objet avec une interface plus précise telle que Element ou Document.

Le code suivant:

package bdav_xml;
 
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
 
public class Main {
 
    public static void main(String[] args) throws ParserConfigurationException, TransformerConfigurationException, TransformerException {
 
        DocumentBuilder db = DocumentBuilderFactory.newInstance().newDocumentBuilder();
 
        Document doc = db.newDocument();
        Element mainEl = doc.createElement("membre");
        doc.appendChild(mainEl);
        mainEl.setAttribute("id", "3");
        Element nom = doc.createElement("nom");
        nom.setTextContent("Rominet");
        mainEl.appendChild(nom);
        Element addresse = doc.createElement("email");
        addresse.setTextContent("sylvestre@eating-birds.com");
        mainEl.appendChild(addresse);
 
        // Affichage, voir la partie sur TrAX
        Transformer copy = TransformerFactory.newInstance().newTransformer();
        copy.transform(new DOMSource(doc), new StreamResult(System.out));
    }
 
}

permet de créer le document XML suivant1):

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<membre id="3">
    <nom>Rominet</nom>
    <email>sylvestre@eating-birds.com</email>
</membre>

Alternativement, le code suivant lit le fichier “rominet.xml” qui contient le document ci-dessus et affiche les données correspondantes:

package bdav_xml;
 
import java.io.IOException;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.xml.sax.SAXException;
 
public class LitXML {
 
    public static void main(String [] args) throws ParserConfigurationException, SAXException, IOException {
        DocumentBuilder db = DocumentBuilderFactory.newInstance().newDocumentBuilder();
 
        Document doc = db.parse("rominet.xml");
        Element rootEl = doc.getDocumentElement();
        int id = Integer.parseInt(rootEl.getAttribute("id"));
        Node nomEl = rootEl.getElementsByTagName("nom").item(0);
        String nom = nomEl.getFirstChild().getNodeValue();
        Node addEl = rootEl.getElementsByTagName("email").item(0);
        String email = addEl.getFirstChild().getNodeValue();
        System.out.println("Membre "+id+", nom: "+nom+", email: "+email);
    }
 
}

Il peut parfois être intéressant d'intégrer le code d'extraction/création XML dans les objets métiers:

package bdav_xml;
 
import org.w3c.dom.Document;
import org.w3c.dom.Element;
 
public class Membre {
 
    private int id;
    private String nom;
    private String email;
 
    // Code metier, getters, setters, constructeurs
    // ...
 
    public Element createXMLElement(Document doc) {
        Element membreEl = doc.createElement("membre");
        membreEl.setAttribute("id", String.valueOf(id));
        Element nomEl = doc.createElement("nom");
        nomEl.setTextContent(nom);
        Element emailEl = doc.createElement("email");
        emailEl.setTextContent(email);
        membreEl.appendChild(nomEl);
        membreEl.appendChild(emailEl);
        return membreEl;
    }
 
    public void setDataFromXML(Element el) {
        id = Integer.parseInt(el.getAttribute("id"));
        nom = el.getElementsByTagName("nom").item(0).getTextContent();
        email = el.getElementsByTagName("email").item(0).getTextContent();
    }
}

Exercice: reprendre le TP sur les ORMs et ajouter aux différentes classes le code permettant de passer du XML aux objets et inversement. Bien réfléchir à la représentation XML, en particulier pour les messages et leur réponses. Créer un programme qui lit une salle et son contenu dans un document XML et sauvegarde ces données dans une base de données via le gestionnaire d'entités.

API TrAX

Il s'agit de la Transformation API for XML. Elle permet de transformer des documents XML. Les documents de départ sont accédés via des Source et les documents d'arrivés sont créés en utilisant des Result. Il en existe différentes sortes, en fonction de la représentation du document XML voulue. En particulier, il existe entre autre des DOMSource et DOMResult, ainsi que des StreamSource et des StreamResult. L'objet s'occupant de la transformation est une instance de la classe Transformer.

La transformation de base2) est une simple copie du document. Elle permet essentiellement de changer de type de représentation, par exemple pour passer d'une représentation en mémoire à une représentation textuelle sous forme de flux. C'est ainsi que l'on peut écrire un arbre DOM dans un fichier:

        Transformer copy = TransformerFactory.newInstance().newTransformer();
        copy.transform(new DOMSource(doc), new StreamResult(new FileOutputStream("rominet.xml")));

Il est cependant possible d'effectuer une transformation de la structure du document via une feuille de style XSLT. Pour cela, il suffit de passer à a méthode newTransformer() un argument correspondant à la feuille de style à utiliser3).

Exercice: toujours dans le cadre du TP ORMs utiliser l'API TrAX pour sauver le contenu d'une salle dans un document XML. Migrer votre application dans une application web sous tomcat. Créer une servlet prenant en argument un numéro de salle et renvoyant4) la représentation XML de la salle correspondante. Créer une feuille de style XSL permettant d'obtenir une vue HTML d'une salle représentée au départ en XML, puis utiliser cette feuille de style dans l'application web pour afficher la vue HTML de la salle demandée.

Remarque: il est possible d'intégrer facilement la feuille de style au programme en la plaçant dans l'arborescence du code Java, ou si on utilise maven dans l'arborescence du répertoire src/main/resources. Si on suppose que la feuille de style salleHTML.xsl se trouve dans le package bdav.xml.xsl, le code suivant permet de créer un Transformer qui va l'appliquer:

        InputStream xslCode = getClass().getResourceAsStream("/bdav/xml/xsl/salleHTML.xsl");
        Source feuilleXsl = new StreamSource(xslCode);
        Transformer toHTML = TransformerFactory.newInstance().newTransformer(feuilleXsl);

Exercice: reprendre le TP JDBC+XML, plus particulièrement le projet Web permettant l'affichage des messages d'une salle en XML. Plutôt que de copier le document obtenu via getCharacterStream(…), utiliser le flux pour construire une StreamSource, puis appliquer à cette dernière une feuille de style pour formater le résultat en HTML, en s'inspirant de l'exercice précédent.

1)
l'indentation a été ajoutée à des fins de lisibilité
2)
le Transformer correspondant peut être obtenu par un appel à TransformerFactory.newInstance().newTransformer()
3)
les feuilles de style XSLT étant elles-mêmes des documents XML, l'argument a le type Source. Typiquement, on utilisera ici un StreamSource.
4)
via une transformation vers response.getWriter()