Parseur DOM et traitement d'erreurs


  1. Principe
    1. Configuration pour la validation
    2. Fonctionnement de la validation
    3. Protocole standard

  2. Exemple
    1. Idée
    2. Arrêt obligatoire en cas de non-validité
    3. Il faut un try enveloppant le tout !


La détection et le signalement des erreurs de syntaxe est une partie essentielle du cahier des charges d'un parseur.
Nous avons vu comment aborder cette question en SAX, mais rien n'a été dit pour DOM !
Or la JavaDoc de DocumentBuilder n'est pas très secourable, elle se borne à déclarer l'envoi de SAXExceptions.

La présente page décrit l'expérience faite en cours M2-IM le 12 janvier 2012, sur la demande d'Uyên-To :
  1. Principe

    1. Configuration pour la validation

      (l'équivalent de la clause 
      parseur.setFeature("http://xml.org/sax/features/validation", true);
      en SAX)

      Elle se fait au niveau de la DocumentBuilderFactory mère du parseur, par le message
      setValidating(true);

    2. Fonctionnement de la validation

      Comme les SAXParseExceptions non-léthales doivent être traitées par un callback,
      il est indipensable d'équiper le parseur d'un ErrorHandler, comme en SAX, par le message
      parseur.setErrorHandler(handler);

      Naturellement, le handler en question doit implémenter l'interface org.xml.sax.ErrorHandler.
      Nous allons illustrer la chose en réutilisant purement et simplement le handler que nous avons écrit pour SAX,
      à savoir la classe SaxVerif.

    3. Protocole standard

              DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
              dbf.setValidating(true);
              DocumentBuilder parseur = dbf.newDocumentBuilder();
              SaxVerif handler = new SaxVerif();
              parseur.setErrorHandler(handler);

              ..... à suivre ....

  2. Exemple

    On part du premier exemple (classe Lire_1, calcul de la moyenne des notes) et on l'agrémente d'une vérification
    que le fichier passé en argument  est correct par rapport à la DTD logée sur le serveur :
    <!DOCTYPE liste SYSTEM "http://pagesperso-systeme.lip6.fr/Jean-Francois.Perrot/inalco/XML/Traductique/Cours2/NomNoteDTD/Nom_note1.dtd">

    1. Idée

      On reprend tout bonnement la procédure main de la classe SaxVerif, et on l'insère dans le main de la classe Lire_1
      ce qui donne, mutatis mutandis :

      ...... suite du code précédent....

      try{                           
         Document doc =  parseur.parse("Nom_note1.xml");
         handler.showErrMsgs(
                 "Validation termine'e avec succe`s !",
                 "Fichier non conforme a` sa DTD" );
        float moy = lire_1(doc);
        System.out.println("\nMoyenne : "+moy);
      }
         catch (SAXException e) {
            // si une erreur fatale a eu lieu
            System.out.println("Erreur fatale ..."+e.getMessage());
            handler.showErrMsgs(
                    "Pas d'autre erreur détectée.",
                    "Auparavant, il y avait déjà des erreurs par rapport à la DTD." );
         }
      }


      Mais cette manière de faire conduit à autoriser le traitement de documents XML corrects, mais non valides,
      ce qui est contraire au but recherché !

    2. Arrêt obligatoire en cas de non-validité

      On va donc ajouter à la méthode showErrMsgs de SaxVerif un envoi d'exception ordinaire dès qu'une erreur
      a été enregistrée :

          public void showErrMsgs(String success, String failure) throws Exception{
              int nbErrs = errMsgs.size();
              if( nbErrs == 0 ){
                  System.out.println(success);
              }else{
                  System.out.println(failure);
                  System.out.println(nbErrs+" erreurs détectées :");
                  for( int i = 1; i <= nbErrs; i++ ){
                         System.out.println(i+". "+ errMsgs.get(i-1));
                  }
                  throw new Exception("Fichier invalide");
              }
          }

         
      et on ajoute dans le main de la classe Lire_1 un catch supplémentaire :
              catch (Exception e){
                System.out.println("Exécution interrompue, donnée incorrecte.");
              }

    3. Il faut un try enveloppant le tout !

      Avec l'écriture ci-dessus, lorsqu'une erreur fatale se produit, l'exception finale est lancée par l'appel
      handler.showErrMsgs(
                    "Pas d'autre erreur détectée.",
                    "Auparavant, il y avait déjà des erreurs par rapport à la DTD." );

      depuis le  catch (SAXException e) {...}
      et non pas depuis le corps du try{...}.
      Du coup, elle n'est pas capturée par le catch (Exception e){...} qui suit !

      Le  catch (Exception e){...} doit donc être hiérarchiquement supérieur à tous les appels à
      handler.showErrMsgs(...), ce qui exige un try enveloppant le tout.

      Code de la nouvelle classe Lire_1.java et du nouveau SaxVerif.java, fichier exemple Nom_note1.xml.