DOM Java Travaux Pratiques

Inria

Apprenez à utiliser une des API fondamentales en réalisant un programme Java.

Naviguez dans un document XML et modifiez-le.

Prérequis

  • Connaître Java

Cours

  • DOM

Programmation du DOM

Zoo

On dispose du document XML :

<?xml version="1.0" encoding="ISO-8859-1" ?>
<!DOCTYPE zoo SYSTEM "zoo.dtd" [
    <!ENTITY % notations SYSTEM "notations.ent">
%notations;
    <!ENTITY flipper.jpg SYSTEM "flipper.jpg" NDATA jpg>
    <!ENTITY oum.jpg SYSTEM "oum.jpg" NDATA jpg>
    <!ENTITY ecco.jpg SYSTEM "ecco.jpg" NDATA jpg>
]>
<!-- Un petit Zoo avec quelques animaux -->
<zoo>
    <info>Ouvert tous les jours de 8h00 à 19h00</info>
    <info><img src="nourrir.gif" />Il est <b>interdit</b> de nourrir les animaux&#127;&#128;.</info>
    <aquarium>
        <mammifères-marins>
            <dauphins>
                <dauphin id="jhgtr13" photo="flipper.jpg" date-naissance="1997-4-1">
                    <nom>Flipper</nom>
                    <sexe>M</sexe>
                    <taille unité="cm">215</taille>
                    <poids unité="kg">105</poids>
                </dauphin>
                <dauphin id="lkjh45" photo="ecco.jpg" date-naissance="2003-10-23">
                    <nom>Ecco</nom>
                    <sexe>F</sexe>
                    <taille unité="cm">202</taille>
                    <poids unité="kg">98</poids>
                </dauphin>
                <dauphin id="kjlhy90" photo="oum.jpg" date-naissance="1996-12-25">
                    <nom>Oum</nom>
                    <sexe>F</sexe>
                    <!-- c'est mon préféré -->
                    <taille unité="cm">295</taille>
                    <poids unité="kg" status="mesure approximative">190<!-- il faut refaire la pesée --></poids>
                </dauphin>
            </dauphins>
        </mammifères-marins>
        <poissons>
            <sélaciens>
                <danger>Il est <b>interdit</b> de nager avec les requins.</danger>
                <requin id="plojk09" espèce="marteau" date-naissance="1998-6-5">
                    <nom>Oussama</nom>
                    <sexe>M</sexe>
                    <taille unité="cm">455</taille>
                    <poids unité="kg">540</poids>
                    <commentaire>A tendance a se jeter contre les murs
                        <![CDATA[<<< marteau & cinglé !!! >>>]]>
                        <!-- Il faudra bien le faire piquer un jour -->
                    </commentaire>
                </requin>
                <requin id="vgyuh43" espèce="requin bleu" nom-savant="carcharias glaucus" date-naissance="2004-1-13">
                    <nom>Saddam</nom>
                    <sexe>M</sexe>
                    <taille unité="cm">355</taille>
                    <poids unité="kg" status="">425</poids>
                </requin>
            </sélaciens>
        </poissons>
    </aquarium>
</zoo>

et de sa DTD :

<?xml version="1.0" encoding="ISO-8859-1" ?>

<!ENTITY nourrir.gif SYSTEM "nourrir.gif" NDATA gif>

<!ENTITY % block "p | info | danger | attention">
<!ENTITY % inline "b | i | em | strong | stabilo | img">
<!ENTITY % attractions "aquarium?, vivarium?, enclos?, volière?">
<!ENTITY % animal "nom, sexe, taille, poids, commentaire?">
<!ENTITY % animal-attr "
    id		ID #REQUIRED
    espèce		CDATA #IMPLIED
    nom-savant	CDATA #IMPLIED
    photo		ENTITY #IMPLIED
    date-naissance  CDATA #REQUIRED
">
<!ENTITY % u-attr "
    unité		CDATA #REQUIRED
    status		CDATA #IMPLIED
">

<!ELEMENT p (#PCDATA | %inline;)*>
<!ELEMENT info (#PCDATA | %inline;)*>
<!ELEMENT danger (#PCDATA | %inline;)*>
<!ELEMENT attention (#PCDATA | %inline;)*>

<!ELEMENT b (#PCDATA | %inline;)*>
<!ELEMENT i (#PCDATA | %inline;)*>
<!ELEMENT em (#PCDATA | %inline;)*>
<!ELEMENT strong (#PCDATA | %inline;)*>
<!ELEMENT stabilo (#PCDATA | %inline;)*>
<!ELEMENT img EMPTY>
<!ATTLIST img src ENTITY #REQUIRED>

<!ELEMENT zoo ((%block;)*, %attractions;)>

<!ELEMENT aquarium (mammifères-marins?, poissons?)>
<!ELEMENT mammifères-marins (dauphins?, baleines?, orques?)>
<!ELEMENT poissons (sélaciens?)>

<!ELEMENT vivarium (serpents?, arachnéens?)>
<!ELEMENT serpents (boas?)>
<!ELEMENT arachnéens (mygales?)>

<!ELEMENT enclos (primates?, fauves?)>
<!ELEMENT primates (gorilles?, orangs-outans?)>
<!ELEMENT fauves (tigres?, lions?)>

<!ELEMENT volière (oiseaux-terrestres?, oiseaux-aquatiques?, rapaces?)>
<!ELEMENT oiseaux-terrestres (autruches?)>
<!ELEMENT oiseaux-aquatiques (pingouins?, manchots?)>
<!ELEMENT rapaces (aigles?)*>

<!ELEMENT dauphins (%block; | dauphin)+>
<!ELEMENT dauphin (%animal;)>
<!ATTLIST dauphin %animal-attr;>

<!ELEMENT baleines (%block; | baleine)+>
<!ELEMENT baleine (%animal;)>
<!ATTLIST baleine %animal-attr;>

<!ELEMENT orques (%block; | orque)+>
<!ELEMENT orque (%animal;)>
<!ATTLIST orque %animal-attr;>

<!ELEMENT sélaciens (%block; | requin)+>
<!ELEMENT requin (%animal;)>
<!ATTLIST requin %animal-attr;>

<!ELEMENT boas (%block; | boa)+>
<!ELEMENT boa (%animal;)>
<!ATTLIST boa %animal-attr;>

<!ELEMENT mygales (%block; | mygale)+>
<!ELEMENT mygale (%animal;)>
<!ATTLIST mygale %animal-attr;>

<!ELEMENT gorilles (%block; | gorille)+>
<!ELEMENT gorille (%animal;)>
<!ATTLIST gorille %animal-attr;>

<!ELEMENT orangs-outans (%block; | orang-outan)+>
<!ELEMENT orang-outan (%animal;)>
<!ATTLIST orang-outan %animal-attr;>

<!ELEMENT tigres (%block; | tigre)+>
<!ELEMENT tigre (%animal;)>
<!ATTLIST tigre %animal-attr;>

<!ELEMENT lions (%block; | lion)+>
<!ELEMENT lion (%animal;)>
<!ATTLIST lion %animal-attr;>

<!ELEMENT autruches (%block; | autruche)+>
<!ELEMENT autruche (%animal;)>
<!ATTLIST autruche %animal-attr;>

<!ELEMENT pingoins (%block; | pingoin)+>
<!ELEMENT pingoin (%animal;)>
<!ATTLIST pingoin %animal-attr;>

<!ELEMENT manchots (%block; | manchot)+>
<!ELEMENT manchot (%animal;)>
<!ATTLIST manchot %animal-attr;>

<!ELEMENT aigles (%block; | aigle)+>
<!ELEMENT aigle (%animal;)>
<!ATTLIST aigle %animal-attr;>

<!ELEMENT nom (#PCDATA)>
<!ELEMENT sexe (#PCDATA)>
<!ELEMENT taille (#PCDATA)>
<!ATTLIST taille %u-attr;>

<!ELEMENT poids (#PCDATA)>
<!ATTLIST poids %u-attr;>

<!ELEMENT commentaire (#PCDATA | %block;)*>

qui utilise une entité :

<?xml version="1.0" encoding="ISO-8859-1" ?>
<!NOTATION jpg SYSTEM "images/jpeg">
<!NOTATION gif SYSTEM "images/gif">

Le document XML décrit les animaux d'un Zoo. On veut faire une visite médicale systématique des jeunes animaux du Zoo. Etudiez et comprenez la structure de l'instance.

Ecrivez un programme en Java avec JAXP qui ajoute un attribut qui ait ce rôle pour les animaux de moins de 2 ans. On n'utilisera pas Document#getElementsByTagName() : on se forcera à parcourir le document d'une manière récursive. Dans un premier temps, on se contentera de modifier le DOM sans enregistrer les modifications.

  • Commencez par écrire le code qui valide le document avec sa DTD avant d'être modifié.
  • Faites un parcours récursif de l'arbre en affichant le nom de chaque élément traversé.
  • Affichez le nom de chaque animal et sa date de naissance.
  • Faites le traitement sur sa date de naissance et mettez à jour le DOM.
  • Complétez votre programme pour qu'il sérialise le document modifié dans un nouveau fichier.

import java.io.*;
import java.util.*;
import java.text.*;
import javax.xml.parsers.*;
import javax.xml.transform.*;
import javax.xml.transform.dom.*;
import javax.xml.transform.stream.*;
import org.w3c.dom.*;
import org.xml.sax.*;

public abstract class DomUpdate {

    // arbre XML
    private Document doc;
    // fichier produit en sortie
    private String output;

    // on parse le document XML
    public DomUpdate(String input, String output) throws ParserConfigurationException, SAXException, IOException {
        // on parse le document XML
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        factory.setValidating( true );
        DocumentBuilder parser = factory.newDocumentBuilder();
        doc = parser.parse( input );
        this.output = output;
    }

    // on traverse tous les noeuds du document
    public void processNodeRecursively(Node node) throws Exception {
        performUpdates( node );
        for (Node child = node.getFirstChild(); child != null; child = child.getNextSibling()) {
            processNodeRecursively(child);
        }
    }

    // on réalise des mises à jour
    abstract public void performUpdates(Node node) throws Exception;

    // on sérialise le document
    public void serialize() throws TransformerConfigurationException, TransformerException {
        TransformerFactory factory = TransformerFactory.newInstance();
        Transformer transformer = factory.newTransformer();
        Source source = new DOMSource( doc );
        Result result = new StreamResult( output );
        transformer.transform( source, result );
    }

    static public void main(final String[] args) {
        try {
            DomUpdate zoo = new DomUpdate(args[0], args[1]) {
                // concrète implémentation de DomUpdate
                // qui cherche les animaux de moins d'un an
                // et leur ajoute la date de visite médicale

                private String DATE_NAISSANCE_ATTR = "date-naissance";
                private DateFormat DATE_FORMAT = new SimpleDateFormat("y-M-d");
                private Calendar TODAY = Calendar.getInstance();
                private String DATE_VISITE_MEDICALE = args[2];

                // le traitement à faire sur les éléments
                public void performUpdates(Node node) throws ParseException {
                    if ( node.getNodeType() == Node.ELEMENT_NODE ) {
                        Element elem = (Element) node;
                        // sont concernés les éléments qui ont un attribut "date-naissance"
                        if ( elem.hasAttribute( DATE_NAISSANCE_ATTR ) ) {
                            // on récupère l'attribut
                            String birthDate = elem.getAttribute( DATE_NAISSANCE_ATTR );
                            Calendar cal = new GregorianCalendar();
                            cal.setTime( DATE_FORMAT.parse( birthDate ) );
                            // on ajoute 1 an à la date de naissance pour comparer avec la date du jour
                            cal.add(Calendar.YEAR, 1);
                            if ( cal.after( TODAY ) ) {
                                // l'animal est jeune : on le marque pour la visite médicale
                                elem.setAttribute("visite-médicale", DATE_VISITE_MEDICALE);
                            }
                        }
                    }
                }
            };

            // on traverse le document
            zoo.processNodeRecursively( zoo.doc.getDocumentElement() );

            // on sérialise le document
            zoo.serialize();
        } catch (Exception e) {
            System.out.println("Usage :\n    DomUpdate in.xml out.xml date");
            e.printStackTrace();
        }
    }

}

Aide Opérations sur les dates

Pour les opérations sur les dates, utiliser java.util.Calendar et pour convertir une chaîne en date, utiliser java.text.SimpleDateFormat.

DateFormat DATE_FORMAT = new SimpleDateFormat( ... );
Date date = DATE_FORMAT.parse( ... );
Calendar TODAY = Calendar.getInstance();
Calendar cal = new GregorianCalendar();
cal.setTime( ... );
// opérations et tests :
cal.add( ... );
if ( cal.after( ... ) ) {
    // ...
}

Aide Sérialisation

Pour sérialiser un document DOM, il suffit de le transformer en utilisant un Transformer qui réalise une copie d'une source DOM vers un flot de caractères.