INaLCO - M2 Ingénierie Multilingue

Techniques XML

Jean-François Perrot

DOM & DTD
en Java

  1. But poursuivi : déclaration de conformité

  2. Génération d'un Document
    1. L'objet générateur
    2. L'espace de noms
    3. La DTD

  3. Sortie sur fichier
    1. Transformation
    2. Objets source.
    3. Objets résultat.
    4. Objet transformateur.
    5. Attention !

  4. Réalisation

But poursuivi : déclaration de conformité

Il s'agit d'engendrer des documents XML conformes à une DTD fixée, avec la possibilité de vérifier cette conformité.
En matière de validation, Java propose un package javax.xml.validation, qui ne s'applique, malheureusement, qu'aux Schémas XML.
Nous n'en dirons donc rien ici.
Nous envisageons deux modes de validation de fichiers XML suivant une DTD :
Pour y parvenir, il y a un certain nombre de détails à régler.
Par exemple, pour la validation par le W3C, il faut engendrer l'en-tête canonique avec indication du codage des caractères
(sous peine d'avertissement), DTD officielle et namespace idem :


<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>....


Pour cela, on devra procéder en deux étapes :
  1. engendrer un objet Document correct (avec DTD et namespace),
    mais naturellement sans indication de charset puisque nous sommes en mémoire centrale ;
  2. l'envoyer dans un fichier avec le codage des caractères voulu,
    et en s'assurant que toutes les informations sont transmises (attention à la DTD !).
Pour les détails de la construction proprement dite de l'ensemble du Document, voir le Bréviaire.

Génération d'un Document

  1. L'objet générateur

    Selon le niveau 2 du DOM, la création des objets Document est la prérogative de l'interface org.w3c.dom.DOMImplementation
    par la méthode createDocument  qui prend trois arguments
    1. String namespaceURI, 
    2. String qualifiedName, 
    3. DocumentType doctype,
    le premier et le troisième pouvant être null. Le qualifiedName en seconde position est celui de l'élément-racine.

    La niveau 2 ne dit pas comment obtenir un objet de type DOMImplementation,
    et le niveau 3 introduit à cette fin une interface supplémentaire DOMImplementationSource... 
    En pratique il faut quitter les spécifications du W3C et entrer dans l'implémentation standard,
    en l'espèce la classe abstraite javax.xml.parsers.DocumentBuilder, qui détient une méthode getDOMImplementation().
    On écrira donc :
    DocumentBuilder parseur = DocumentBuilderFactory.newInstance().newDocumentBuilder();
    DOMImplementation domi = parseur.getDOMImplementation();
    et on demandera Document doc = domi.createDocument (....)
  2. L'espace de noms

    intervient sous deux aspects :
    Par exemple pour engendrer la racine d'une feuille de style XSLT avec le préfixe usuel xsl
    <xsl:stylesheet
       xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version='1.0'>

    on devra commander
    domi.createDocument("http://www.w3.org/1999/XSL/Transform","xsl:stylesheet", null).

    Bien entendu, la viduité du préfixe se traduit par l'absence du séparateur ':',
    et pour obtenir la racine du document XHTML ci-dessus <html xmlns="http://www.w3.org/1999/xhtml">,
    on demandera
    domi.createDocument("http://www.w3.org/1999/xhtml","html", dtd ).
    Voyons à présent comment construire l'objet dtd.

  3. La DTD est un objet de type org.w3c.dom.DocumentType

    il est également (selon DOM niveau 2) synthétisé par DOMImplementation, avec la méthode createDocumentType
    qui prend 3 arguments
    1. String qualifiedName,
      le nom de la racine du document (avec son préfixe le cas échéant)
    2. String publicId,
      l'identificateur public (p. ex. "-//W3C//DTD XHTML 1.0 Strict//EN")
    3. String systemId
      qui est l'URL repérant la DTD, p. ex. "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd".
    L'un des deux Ids peut être null.

    Ainsi, pour obtenir l'objet DTD nécessaire à notre page XHTML-strict, nous demanderons :
    DocumentType dtd = domi.createDocumentType(
             "html", 
              "-//W3C//DTD XHTML 1.0 Strict//EN",
              "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"
    );

    ce qui termine notre construction.

    En revanche, pour une modeste DTD à usage privé repérée par
    "http://pagesperso-systeme.lip6.fr/Jean-Francois.Perrot/inalco/XML/Traductique/Cours2/NomNoteDTD/Nom_note2.dtd",
    nous demanderons :
    DocumentType dtd = domi.createDocumentType("liste", null, "http://.....");
    avant de créer notre Document par domi.createDocument(null,"liste", dtd);

Sortie sur fichier

  1. Transformation

    On peut bien entendu programmer soi-même l'impression caractère par caractère de son arbre DOM.
    Mais à moins que l'on poursuive un but particulier, pourquoi réinventer la roue ?
    S'il s'agit simplement de transférer l'arbre sur un support permanent et communicable,
    il est évidemment préférable d'utiliser les outils standard.
    N.B. La sortie ainsi produite ne contient évidemment aucun blanc ni saut de ligne
    qui ne serait pas déjà dans le document source !

    La sortie sur fichier est vue par JAXP comme un cas particulier de transformation,
    catégorie qui contient aussi les transformations XSLT.
    La notion de transformation suppose définies une notion de source et une notion de résultat.
    Notre opération de sortie sur fichier fait donc appel au package javax.xml.transform
    et à ses sous-packages javax.xml.transform.stream et javax.xml.transform.dom.
  2. Objets source.

    Ils sont instance de la classe javax.xml.transform.dom.DOMSource,
    et créés simplement à partir d'un objet DOM Document doc par :
    DOMSource ds = new DOMSource(doc);
    ou plus généralement à partir d'un sous-arbre quelconque, repéré par un Node sommet :
    DOMSource dsa = new DOMSource(sommet);

    N.B. Les mêmes objets DOMSource servent de données pour la méthode validate
    de javax.xml.validation.Validator.
  3. Objets résultat.

    Ils sont instance de la classe javax.xml.transform.stream.StreamResult.
    De manière à contrôler explicitement le codage des caractères (indispensable pour nous),
    on les les créera systématiquement à partir d'un objet java.io.Writer out,
    par StreamResult res = new StreamResult(out);

    le Writer en question étant construit en fonction de la situation :
  4. Objet transformateur.

    Instance de la classe abstraite javax.xml.transform.Transformer.
    Conformément à la démarche générale de JAXP, on l'obtient à partir de
    Transformer trans = TransformerFactory.newInstance().newTransformer();

    Après quoi il n'y a plus qu'à demander : trans.transform(ds, res);
    et le tour est joué !

  5. Attention !

    !Pour une raison obscure, la DOCTYPE n'est pas écrite dans le fichier de sortie !
    Pour l'obtenir, il faut une manœuvre supplémentaire :
    il faut avoir les deux identificateurs publicId et systemId de notre DTD,
    et procéder à deux réglages de l'objet Transformer (appelons-le trans comme ci-dessus)

    où on se réfère à la classe javax.xml.transform.OutputKeys
    avant
    de lancer trans.transform(ds, res);...

    Par exemple, pour la DTD de XHTML-strict on écrira :
    trans.setOutputProperty(OutputKeys.DOCTYPE_PUBLIC, "-//W3C//DTD XHTML 1.0 Strict//EN");
    et
    trans.setOutputProperty(OutputKeys.DOCTYPE_SYSTEM, "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd");

    Si on ne dispose que d'un seul des deux  publId et sysId, un seul des deux réglages suffit.

Réalisation

L'ensemble de ces dispositions se trouve proprement enveloppé dans
une batterie de méthodes de classe de la classe AuxDOM,
dont on trouvera un exemple de mise en œuvre dans un remake de la transformation UnVersDeux.