INaLCO - M2 Ingénierie Multilingue
Techniques XML
Jean-François Perrot
DOM & DTD
en Java
- But
poursuivi : déclaration de conformité
- Génération
d'un Document
- L'objet
générateur
- L'espace
de noms
- La
DTD
- Sortie
sur fichier
- Transformation
- Objets
source.
- Objets
résultat.
- Objet
transformateur.
- Attention
!
- 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 :
- engendrer un objet
Document
correct (avec DTD et namespace),
mais naturellement sans indication de charset
puisque nous sommes en mémoire centrale ;
- 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
-
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
String namespaceURI,
String qualifiedName,
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
(....)
-
L'espace
de noms
intervient sous deux aspects :
- son URI qui apparaît en premier argument
- son préfixe associé, qui fait partie du
qualifiedName
de la racine, de la forme préfixe:nom_local
.
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
.
-
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
String qualifiedName
,
le nom de la racine du document (avec son préfixe le cas échéant)
String publicId
,
l'identificateur public (p. ex. "-//W3C//DTD XHTML
1.0 Strict//EN"
)
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 Id
s 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
-
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.
-
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.
-
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 :
- pour imprimer sur la sortie standard :
Writer out
=
new OutputStreamWriter(System.out, "UTF-8");
- pour envoyer dans un fichier nommé
nomFich
:
Writer out
=
new OutputStreamWriter(
new
FileOutputStream(nomFich), "UTF-8");
-
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é !
-
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)
- trans.setOutputProperty(OutputKeys.DOCTYPE_PUBLIC,
publicId);
- trans.setOutputProperty(OutputKeys.DOCTYPE_SYSTEM,
systemId);
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
.