Skip to content

6. XML und Java

In diesem Kapitel stellen wir die Verwendung von XML-Dokumenten mit Java vor. Wir tun dies im Zusammenhang mit der im vorigen Kapitel behandelten Steueranwendung.

6.1. XML-Dateien und XSL-Stylesheets

Betrachten Sie die folgende XML-Datei, simulations.xml, die die Ergebnisse von Steuerberechnungssimulationen darstellen könnte:

<?xml version="1.0" encoding="ISO-8859-1"?>
<simulations>
  <simulation marie="oui" enfants="2" salaire="200000" impot="22504"/>
  <simulation marie="non" enfants="2" salaire="200000" impot="33388"/>
</simulations>

Bei der Anzeige mit IE 6 ergibt sich folgendes Ergebnis:

Image

IE6 erkennt, dass es sich um eine XML-Datei handelt (dank der Dateiendung .xml), und formatiert sie auf seine eigene Weise. Mit Netscape erhält man eine leere Seite. Wenn man sich jedoch den Quellcode ansieht (Ansicht/Quelltext), sieht man die ursprüngliche XML-Datei:

Image

Warum zeigt Netscape nichts an? Weil es ein Stylesheet benötigt, das ihm mitteilt, wie die XML-Datei in eine HTML-Datei umgewandelt werden soll, die es dann anzeigen kann. Es stellt sich heraus, dass IE 6 über ein Standard-Stylesheet verfügt, wenn die XML-Datei kein eigenes bereitstellt, was hier der Fall war.

Es gibt eine Sprache namens XSL (eXtended StyleSheet Language), mit der man die Umwandlungen beschreiben kann, die erforderlich sind, um eine XML-Datei in eine beliebige Textdatei zu konvertieren. XSL unterstützt zahlreiche Anweisungen und ähnelt stark Programmiersprachen. Wir werden hier nicht ins Detail gehen, da dies Dutzende von Seiten in Anspruch nehmen würde. Wir werden lediglich zwei Beispiele für XSL-Stylesheets beschreiben. Das erste ist dasjenige, das die XML-Datei simulations.xml in HTML-Code umwandelt. Wir ändern letztere so, dass sie das Stylesheet angibt, das Browser verwenden können, um sie in ein HTML-Dokument umzuwandeln, das sie dann anzeigen können:

<?xml version="1.0" encoding="ISO-8859-1" ?>
<?xml-stylesheet type="text/xsl" href="simulations.xsl"?>
<simulations>
  <simulation marie="oui" enfants="2" salaire="200000" impot="22504"/>
  <simulation marie="non" enfants="2" salaire="200000" impot="33388"/>
</simulations>

Der XML-Befehl

<?xml-stylesheet type="text/xsl" href="simulations.xsl"?>

bezeichnet die Datei simulations.xsl als XML-Stylesheet vom Typ text/xsl, d. h. als Textdatei, die XSL-Code enthält. Dieses Stylesheet wird von Browsern verwendet, um den XML-Text in ein HTML-Dokument umzuwandeln. Hier ist das Ergebnis, das mit Netscape 7 beim Laden der XML-Datei simulations.xml erzielt wurde:

Image

Wenn wir den Quellcode des Dokuments anzeigen (Ansicht/Quelltext), sehen wir das ursprüngliche XML-Dokument und nicht das angezeigte HTML-Dokument:

Image

Netscape verwendete das Stylesheet „simulations.xsl“, um das obige XML-Dokument in ein anzeigbares HTML-Dokument umzuwandeln. Nun ist es an der Zeit, uns den Inhalt dieses Stylesheets anzusehen:

<?xml version="1.0" encoding="ISO-8859-1"?>
  <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
      <xsl:output method="html" indent="yes"/>

    <xsl:template match="/">
        <html>
          <head>
            <title>Simulations de calculs d'impôts</title>
        </head>
          <body>
            <center>
              <h3>Simulations de calculs d'impôts</h3>
            <hr/>
            <table border="1">
                    <th>marié</th><th>enfants</th><th>salaire</th><th>impôt</th>
                          <xsl:apply-templates select="/simulations/simulation"/>
                        </table>
         </center>
       </body>
     </html>
        </xsl:template>


    <xsl:template match="simulation">
          <tr>
            <td><xsl:value-of select="@marie"/></td>
            <td><xsl:value-of select="@enfants"/></td>
            <td><xsl:value-of select="@salaire"/></td>
            <td><xsl:value-of select="@impot"/></td>
        </tr>
       </xsl:template>
  </xsl:stylesheet>
  • Ein XSL-Stylesheet ist eine XML-Datei und folgt daher den XML-Regeln. Unter anderem muss es „wohlgeformt“ sein, was bedeutet, dass jeder öffnende Tag geschlossen werden muss.
  • Die Datei beginnt mit zwei XML-Direktiven, die in jedem XSL-Stylesheet enthalten sein können:
<?xml version="1.0" encoding="ISO-8859-1"?>
  <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

Das Attribut encoding="ISO-8859-1" ermöglicht die Verwendung von Zeichen mit Akzenten im Stylesheet.

  • Das Tag <xsl:output method="html" indent="yes"/> teilt dem XSL-Interpreter mit, dass „eingerückter“ HTML-Code erzeugt werden soll.
  • Das Tag <xsl:template match="element"> dient dazu, das Element im XML-Dokument zu definieren, auf das die Anweisungen zwischen <xsl:template ...> und </xsl:template> angewendet werden sollen.
    <xsl:template match="/">
................
        </xsl:template>

Im obigen Beispiel bezeichnet das Element „/“ die Wurzel des Dokuments. Das bedeutet, dass, sobald der Anfang des XML-Dokuments erreicht wird, die zwischen den beiden Tags befindlichen XSL-Befehle ausgeführt werden.

  • Alles, was kein XSL-Tag ist, wird unverändert in den Ausgabestrom aufgenommen. Die XSL-Tags selbst werden ausgeführt. Einige von ihnen erzeugen ein Ergebnis, das in den Ausgabestrom aufgenommen wird. Betrachten wir das folgende Beispiel:
    <xsl:template match="/">
        <html>
          <head>
            <title>Simulations de calculs d'impôts</title>
        </head>
          <body>
            <center>
              <h3>Simulations de calculs d'impôts</h3>
            <hr/>
            <table border="1">
                    <th>marié</th><th>enfants</th><th>salaire</th><th>impôt</th>
                          <xsl:apply-templates select="/simulations/simulation"/>
                        </table>
          </center>
        </body>
      </html>
        </xsl:template>

Beachten Sie, dass das zu analysierende XML-Dokument wie folgt lautet:

<?xml version="1.0" encoding="ISO-8859-1"?>
<simulations>
  <simulation marie="oui" enfants="2" salaire="200000" impot="22504"/>
  <simulation marie="non" enfants="2" salaire="200000" impot="33388"/>
</simulations>

Ab dem Anfang des geparsten XML-Dokuments (match="/") gibt der XSL-Prozessor den Text

<html>
          <head>
            <title>Simulations de calculs d'impôts</title>
        </head>
          <body>
            <center>
              <h3>Simulations de calculs d'impôts</h3>
            <hr>
            <table border="1">
                    <th>marié</th><th>enfants</th><th>salaire</th><th>impôt</th>

Beachten Sie, dass im Originaltext <hr/> stand und nicht <hr>. Im Originaltext konnten wir nicht <hr> schreiben, da dies zwar ein gültiger HTML-Tag, aber ein ungültiger XML-Tag ist. Wir haben es hier jedoch mit XML-Text zu tun, der „wohlgeformt“ sein muss, was bedeutet, dass jeder Tag geschlossen werden muss. Wir schreiben daher <hr/>, und da wir <xsl:output text="html ...> geschrieben haben, wandelt der XSL-Prozessor den Text <hr/> in <hr> um. Auf diesen Text folgt der durch den XSL-Befehl erzeugte Text:

      <xsl:apply-templates select="/simulations/simulation"/>

Was dieser Text ist, werden wir später sehen. Schließlich fügt der Interpreter den Text hinzu:

                    </table>
          </center>
        </body>
      </html>

Die Anweisung <xsl:apply-templates select="/simulations/simulation"/> weist den XSL-Prozessor an, die „Vorlage“ auf das Element /simulations/simulation anzuwenden. Sie wird jedes Mal ausgeführt, wenn der XSL-Interpreter innerhalb eines <simulations>..</simulations>-Tags im geparsten XML-Text auf ein <simulation>..</simulations>- oder <simulation/>-Tag stößt. Beim Auftreffen des <simulation>-Tags führt der Interpreter die Anweisungen der folgenden Vorlage aus:

    <xsl:template match="simulation">
          <tr>
            <td><xsl:value-of select="@marie"/></td>
            <td><xsl:value-of select="@enfants"/></td>
            <td><xsl:value-of select="@salaire"/></td>
            <td><xsl:value-of select="@impot"/></td>
        </tr>
       </xsl:template>

Betrachten Sie die folgenden XML-Zeilen:

<simulations>
  <simulation marie="oui" enfants="2" salaire="200000" impot="22504"/>

Die Zeile <simulation ..> entspricht der Vorlage für die XSL-Anweisung <xsl:apply-templates select="/simulations/simulation">. Der XSL-Interpreter wird daher versuchen, die Anweisungen anzuwenden, die dieser Vorlage entsprechen. Er findet die Vorlage <xsl:template match="simulation"> und führt sie aus. Zur Erinnerung: Alles, was kein XSL-Befehl ist, wird vom XSL-Interpreter unverändert weitergeleitet, während XSL-Befehle durch das Ergebnis ihrer Ausführung ersetzt werden. Die XSL-Anweisung <xsl:value-of select="@champ"/> wird somit durch den Wert des Attributs „champ“ des geparsten Knotens (hier ein <simulation>-Knoten) ersetzt. Das Parsen der vorherigen XML-Zeile erzeugt die folgende Ausgabe:

XSL
Ausgabe
<tr><td>
<tr><td>
<xsl:value-of select="@marie"/>
ja
</td><td>
</td><td>
<xsl:value-of select="@children"/>
2
</td><td>
</td><td>
<xsl:value-of select="@salary"/>
200000
</td><td>
</td><td>
<xsl:value-of select="@tax"/>
22504
</td></tr>
</td></tr>

Insgesamt ergibt die XML-Zeile

<simulation marie="oui" enfants="2" salaire="200000" impot="22504"/>

wird in die folgende HTML-Zeile umgewandelt:

<tr><td>oui</td><td>2</td><td>200000</td><td>22504</td></tr>

All diese Erklärungen sind zwar etwas rudimentär, aber dem Leser sollte nun klar sein, dass der folgende XML-Text:

<?xml version="1.0" encoding="ISO-8859-1"?>
<?xml-stylesheet type="text/xsl" href="simulations.xsl"?>
<simulations>
  <simulation marie="oui" enfants="2" salaire="200000" impot="22504"/>
  <simulation marie="non" enfants="2" salaire="200000" impot="33388"/>
</simulations>

begleitet von dem folgenden XSL-Stylesheet simulations.xsl:

<?xml version="1.0" encoding="ISO-8859-1"?>
  <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
      <xsl:output method="html" indent="yes"/>

    <xsl:template match="/">
        <html>
          <head>
            <title>Simulations de calculs d'impôts</title>
        </head>
          <body>
            <center>
              <h3>Simulations de calculs d'impôts</h3>
            <hr/>
            <table border="1">
                    <th>marié</th><th>enfants</th><th>salaire</th><th>impôt</th>
                          <xsl:apply-templates select="/simulations/simulation"/>
                        </table>
         </center>
       </body>
     </html>
        </xsl:template>


    <xsl:template match="simulation">
          <tr>
            <td><xsl:value-of select="@marie"/></td>
            <td><xsl:value-of select="@enfants"/></td>
            <td><xsl:value-of select="@salaire"/></td>
            <td><xsl:value-of select="@impot"/></td>
        </tr>
       </xsl:template>
  </xsl:stylesheet>

erzeugt den folgenden HTML-Text:

<html>
<head>
<title>Simulations de calculs d'impôts</title>
</head>
<body>
<center>
<h3>Simulations de calculs d'impots</h3>
<hr>
<table border="1">
<th>marié</th><th>enfants</th><th>salaire</th><th>impôt</th>
<tr>
<td>oui</td><td>2</td><td>200000</td><td>22504</td>
</tr>
<tr>
<td>non</td><td>2</td><td>200000</td><td>33388</td>
</tr>
</table>
</center>
</body>
</html>

Die XML-Datei simulations.xml wird zusammen mit dem Stylesheet simulations.xsl in einem modernen Browser (hier Netscape 7) wie folgt angezeigt:

Image

6.2. Steuerberechnungsanwendung: Version 6

6.2.1. Die XML-Dateien und XSL-Stylesheets der Steuerberechnungsanwendung

Kehren wir zur Steuer-Webanwendung zurück und passen wir sie so an, dass die an die Clients gesendete Antwort im XML-Format statt im HTML-Format vorliegt. Dieser XML-Antwort wird ein XSL-Stylesheet beigefügt, damit Browser sie anzeigen können. Im vorherigen Abschnitt haben wir Folgendes vorgestellt:

  • die Datei simulations.xml, die einen Prototyp einer XML-Antwort mit Simulationen zur Steuerberechnung enthält
  • die Datei simulations.xsl, die das dieser XML-Antwort beigefügte XSL-Stylesheet darstellt

Wir müssen auch den Fall einer Antwort berücksichtigen, die Fehler enthält. Der Prototyp für die XML-Antwort in diesem Fall ist die folgende Datei errors.xml:

<?xml version="1.0" encoding="windows-1252"?>
<?xml-stylesheet type="text/xsl" href="erreurs.xsl"?>
<erreurs>
    <erreur>erreur 1</erreur>
  <erreur>erreur 2</erreur>
</erreurs>

Das Stylesheet errors.xsl, das zur Anzeige dieses XML-Dokuments in einem Browser verwendet wird, sieht wie folgt aus:

<?xml version="1.0" encoding="windows-1252"?>
  <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
      <xsl:output method="html" indent="yes"/>

    <xsl:template match="/">
        <html>
          <head>
            <title>Simulations de calculs d'impôts</title>
        </head>
          <body>
            <center>
              <h3>Simulations de calculs d'impôts</h3>
          </center>            
          <hr/>
          Les erreurs suivantes se sont produites :
          <ul>
                  <xsl:apply-templates select="/erreurs/erreur"/>
              </ul>            
        </body>
      </html>
        </xsl:template>

    <xsl:template match="erreur">
          <li><xsl:value-of select="."/></li>
        </xsl:template>
</xsl:stylesheet>    

Dieses Stylesheet führt einen bisher noch nicht behandelten XSL-Befehl ein: <xsl:value-of select="."/>. Dieser Befehl gibt den Wert des analysierten Knotens aus, in diesem Fall eines <error>text</error>-Knotens. Der Wert dieses Knotens ist der Text zwischen dem öffnenden und dem schließenden Tag, in diesem Fall „text“.

Der Code in errors.xml wird durch das Stylesheet errors.xsl in das folgende HTML-Dokument umgewandelt:

<html>
<head>
<title>Simulations de calculs d'impots</title>
</head>
<body>
<center>
<h3>Simulations de calculs d'impots</h3>
</center>
<hr>
          Les erreurs suivantes se sont produites :
<ul>
<li>erreur 1</li>
<li>erreur 2</li>
</ul>
</body>
</html>

Die Datei „errors.xml“ wird zusammen mit ihrem Stylesheet von einem Browser wie folgt angezeigt:

Image

6.2.2. Das Servlet „xmlsimulations“

Wir erstellen eine Datei index.html und legen sie im Anwendungsverzeichnis impots ab. Die angezeigte Seite sieht wie folgt aus:

Image

Dieses HTML-Dokument ist ein statisches Dokument. Sein Code lautet wie folgt:

<html>
    <head>
      <title>impots</title>
    <script language="JavaScript" type="text/javascript">
        function effacer(){
          // raz du formulaire
        with(document.frmImpots){
            optMarie[0].checked=false;
          optMarie[1].checked=true;
          txtEnfants.value="";
          txtSalaire.value="";
          txtImpots.value="";
        }//with
      }//effacer

      function calculer(){
          // vérification des paramètres avant de les envoyer au serveur
        with(document.frmImpots){
          //nbre d'enfants
          champs=/^\s*(\d+)\s*$/.exec(txtEnfants.value);
          if(champs==null){
            // le modéle n'est pas vérifié
            alert("Le nombre d'enfants n'a pas été donné ou est incorrect");
            nbEnfants.focus();
            return;
          }//if
          //salaire
          champs=/^\s*(\d+)\s*$/.exec(txtSalaire.value);
          if(champs==null){
            // le modéle n'est pas vérifié
            alert("Le salaire n'a pas été donné ou est incorrect");
            salaire.focus();
            return;
          }//if
          // c'est bon - on envoie
          submit();
        }//with
      }//calculer        
        </script>
  </head>

  <body background="/impots/images/standard.jpg">
      <center>
        Calcul d'impôts
        <hr>
      <form name="frmImpots" action="/impots/xmlsimulations" method="POST">
          <table>
            <tr>
              <td>Etes-vous marié(e)</td>
            <td>
                <input type="radio" name="optMarie" value="oui">oui
              <input type="radio" name="optMarie" value="non" checked>non
            </td>
          </tr>
          <tr>
              <td>Nombre d'enfants</td>
            <td><input type="text" size="3" name="txtEnfants" value=""></td>
          </tr>
          <tr>
              <td>Salaire annuel</td>
            <td><input type="text" size="10" name="txtSalaire" value=""></td>
          </tr>
          <tr></tr>
          <tr>
              <td><input type="button" value="Calculer" onclick="calculer()"></td>
            <td><input type="button" value="Effacer" onclick="effacer()"></td>
          </tr>
        </table>
      </form>
    </center>
 </body>
</html>

Beachten Sie, dass die Formulardaten an die URL /impots/xmlsimulations gesendet werden. Bei dieser Anwendung handelt es sich um ein Java-Servlet, das in der Datei web.xml der Anwendung impots wie folgt konfiguriert ist:

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

<!DOCTYPE web-app
    PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
    "http://java.sun.com/dtd/web-app_2_3.dtd">

<web-app>
...........
    <servlet>
      <servlet-name>xmlsimulations</servlet-name>
    <servlet-class>xmlsimulations</servlet-class>
    <init-param>
          <param-name>xslSimulations</param-name>
        <param-value>simulations.xsl</param-value>
    </init-param>
    <init-param>
          <param-name>xslErreurs</param-name>
        <param-value>erreurs.xsl</param-value>
    </init-param>

    <init-param>
          <param-name>DSNimpots</param-name>
        <param-value>mysql-dbimpots</param-value>
    </init-param>
    <init-param>
          <param-name>admimpots</param-name>
        <param-value>admimpots</param-value>
    </init-param>
    <init-param>
          <param-name>mdpimpots</param-name>
        <param-value>mdpimpots</param-value>
    </init-param>    
  </servlet>
........
  <servlet-mapping>
      <servlet-name>xmlsimulations</servlet-name>
    <url-pattern>/xmlsimulations</url-pattern>
  </servlet-mapping>
</web-app>
  • Das Servlet heißt xmlsimulations und basiert auf der Klasse xmlsimulations.class.
  • Seine Parameter sind DSNimpots, admimpots und mdpimpots, die für den Zugriff auf die Steuerdatenbank erforderlich sind. Zusätzlich akzeptiert es zwei weitere Parameter:
    • xslSimulations, der Name der Stylesheet-Datei, die der XML-Antwort mit den Simulationen beigefügt werden muss
    • xslErrors, der Name des Stylesheets, das der XML-Antwort mit etwaigen Fehlern beiliegen muss
  • Es hat einen Alias, xmlsimulations, wodurch es über die URL http://localhost:8080/impots/xmlsimulations erreichbar ist.

Das Grundgerüst des xmlsimulations-Servlets ähnelt dem des bereits besprochenen simulations-Servlets. Der Hauptunterschied besteht darin, dass es XML statt HTML generieren muss. Dies führt zur Entfernung der in früheren Anwendungen verwendeten JSP-Dateien. Ihre Hauptaufgabe bestand darin, die Lesbarkeit des generierten HTML-Codes zu verbessern, indem verhindert wurde, dass dieser im Java-Code des Servlets verborgen bleibt. Diese Funktion ist nun nicht mehr erforderlich. Das Servlet muss zwei Arten von XML-Code generieren:

  • einen für die Simulationen
  • einen für Fehler

Wir haben zuvor die beiden Arten von XML-Antworten vorgestellt und untersucht, die in diesen beiden Fällen bereitgestellt werden müssen, sowie die dazugehörigen Stylesheets. Der Servlet-Code lautet wie folgt:

import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
import java.util.regex.*;
import java.util.*;

public class xmlsimulations extends HttpServlet{

    // instance variables
    String msgErreur=null;
    String xslSimulations=null;
    String xslErreurs=null;
    String DSNimpots=null;
    String admimpots=null;
    String mdpimpots=null;
    impotsJDBC impots=null;

    //-------- GET
    public void doGet(HttpServletRequest request, HttpServletResponse response)
    throws IOException, ServletException{

        // retrieve the write stream to the client
        PrintWriter out=response.getWriter();

        // specify the type of response
        response.setContentType("text/xml");

         // error list
        ArrayList erreurs=new ArrayList();

         // was the initialization successful?
        if(msgErreur!=null){
             // that's it - we send the response with errors to the server
            erreurs.add(msgErreur);
            sendErreurs(out,xslErreurs,erreurs);
             // it's over
            return;
        }

         // retrieve previous simulations from the session
        HttpSession session=request.getSession();
        ArrayList simulations=(ArrayList)session.getAttribute("simulations");
        if(simulations==null) simulations=new ArrayList();

        // retrieve the parameters of the current query
        String optMarie=request.getParameter("optMarie");              // marital status
        String txtEnfants=request.getParameter("txtEnfants");       // no. of children
        String txtSalaire=request.getParameter("txtSalaire");       // annual salary

         // do we have all the expected parameters
        if(optMarie==null || txtEnfants==null || txtSalaire==null){
             // missing parameters
             // send response with errors
            erreurs.add("Demande incomplète. Il manque des paramètres");
            sendErreurs(out,xslErreurs,erreurs);
             // it's over
            return;
        }

         // we have all the parameters - we check them
         // marital status
        if( ! optMarie.equals("oui") && ! optMarie.equals("non")){
             // error
            erreurs.add("Etat marital incorrect");
        }
         // number of children
        txtEnfants=txtEnfants.trim();
        if(! Pattern.matches("^\\d+$",txtEnfants)){
            // error
            erreurs.add("Nombre d'enfants incorrect");
        }
         // salary
        txtSalaire=txtSalaire.trim();
        if(! Pattern.matches("^\\d+$",txtSalaire)){
            // error
            erreurs.add("Salaire incorrect");
        }

        if(erreurs.size()!=0){
            // if there are errors, we report them
            sendErreurs(out,xslErreurs,erreurs);
        }else{
             // no errors
            try{
                 // you can calculate the tax payable
                int nbEnfants=Integer.parseInt(txtEnfants);
                int salaire=Integer.parseInt(txtSalaire);
                String txtImpots=""+impots.calculer(optMarie.equals("oui"),nbEnfants,salaire);
                 // the current result is added to the previous simulations
                String[] simulation={optMarie.equals("oui") ? "oui" : "non",txtEnfants, txtSalaire, txtImpots};
                simulations.add(simulation);
                 // we send the answer with simulations
                sendSimulations(out,xslSimulations,simulations);
            }catch(Exception ex){}
        }//if-else
         // we put the list of simulations back into the session
        session.setAttribute("simulations",simulations);
    }//GET

     //-------- POST
    public void doPost(HttpServletRequest request, HttpServletResponse response)
    throws IOException, ServletException{
        doGet(request,response);
    }//POST

     //-------- INIT
    public void init(){
         // retrieve initialization parameters
        ServletConfig config=getServletConfig();
        xslSimulations=config.getInitParameter("xslSimulations");
        xslErreurs=config.getInitParameter("xslErreurs");
        DSNimpots=config.getInitParameter("DSNimpots");
        admimpots=config.getInitParameter("admimpots");
        mdpimpots=config.getInitParameter("mdpimpots");

         // parameters ok?
        if(xslSimulations==null || DSNimpots==null || admimpots==null || mdpimpots==null){
            msgErreur="Configuration incorrecte";
            return;
        }

         // create an instance of impotsJDBC
        try{
            impots=new impotsJDBC(DSNimpots,admimpots,mdpimpots);
        }catch(Exception ex){
            msgErreur=ex.getMessage();
        }
    }//init

     //-------- sendErreurs
    private void sendErreurs(PrintWriter out,String xslErreurs,ArrayList erreurs){
        String réponse="<?xml version=\"1.0\" encoding=\"windows-1252\"?>"
                    + "<?xml-stylesheet type=\"text/xsl\" href=\""+xslErreurs+"\"?>\n"
              +"<erreurs>\n";
        for(int i=0;i<erreurs.size();i++){
            réponse+="<erreur>"+(String)erreurs.get(i)+"</erreur>\n";
        }//for
        réponse+="</erreurs>\n";
         // we send the answer
        out.println(réponse);
    }

     //-------- sendSimulations
    private void sendSimulations(PrintWriter out, String xslSimulations, ArrayList simulations){
        String réponse="<?xml version=\"1.0\" encoding=\"windows-1252\"?>"
                    + "<?xml-stylesheet type=\"text/xsl\" href=\""+xslSimulations+"\"?>\n"
              + "<simulations>\n";
        String[] simulation=null;
        for(int i=0;i<simulations.size();i++){
            // simulation no. i
            simulation=(String[])simulations.get(i);
            réponse+="<simulation "
                     +"marie=\""+(String)simulation[0]+"\" "
                     +"enfants=\""+(String)simulation[1]+"\" "
                     +"salaire=\""+(String)simulation[2]+"\" "
                 +"impot=\""+(String)simulation[3]+"\" />\n";
        }//for
        réponse+="</simulations>\n";
         // we send the answer
        out.println(réponse);
    }
}

Schauen wir uns die wichtigsten neuen Funktionen dieses Codes im Vergleich zu dem an, was wir bereits kannten:

  • Die init-Prozedur ruft neue Parameter aus der Konfigurationsdatei web.xml ab: Die Namen der beiden XSL-Stylesheets, die der Antwort beigefügt werden müssen, werden in den Variablen xslSimulations und xslErrors gespeichert. Bei diesen beiden Stylesheets handelt es sich um die zuvor besprochenen Dateien simulations.xsl und errors.xsl. Sie befinden sich im Anwendungsverzeichnis impots:
dos>dir E:\data\serge\Servlets\impots\*.xsl
27/08/2002  08:15                1 030 simulations.xsl
27/08/2002  09:23                  795 erreurs.xsl
  • Die GET-Prozedur beginnt mit der Überprüfung, ob während der Initialisierung ein Fehler aufgetreten ist. Ist dies der Fall, ruft sie die Prozedur sendErrors auf, die die für diesen Fall passende XML-Antwort generiert und anschließend beendet wird. Die XML-Antwort enthält ein Tag, das das zu verwendende Stylesheet angibt.
  • Wenn keine Fehler aufgetreten sind, analysiert die GET-Prozedur die Parameter der Client-Anfrage. Wenn sie einen Fehler findet, meldet sie diesen mithilfe der sendErrors-Prozedur. Andernfalls berechnet sie die neue Simulation, fügt sie zu den in der aktuellen Sitzung gespeicherten vorherigen hinzu und beendet den Vorgang, indem sie ihre XML-Antwort über die sendSimulations-Prozedur sendet. Letztere verläuft analog zur sendErrors-Prozedur.
  • Beachten Sie, dass das Servlet seine Antwort als Typ text/xml deklariert:
        // on précise le type de la réponse
        response.setContentType("text/xml");

Hier sind einige Ausführungsbeispiele. Das Ausgangsformular wird wie folgt ausgefüllt:

Image

Die MySQL-Datenbank wurde nicht gestartet, sodass das imports-Objekt in der init-Prozedur des Servlets nicht erstellt werden kann. Die Antwort des Servlets lautet daher wie folgt:

Image

Der vom Browser empfangene Code (Ansicht/Quelltext) lautet wie folgt:

Image

Wenn wir nun nach dem Start der MySQL-Datenbank zwei weitere Simulationen ausführen, erhalten wir folgendes Ergebnis:

Image

Diesmal erhielt der Browser den folgenden Code:

Image

Beachten Sie, dass unsere neue Anwendung aufgrund des Wegfalls der JSP-Dateien einfacher ist als zuvor. Ein Teil der Aufgaben, die zuvor von diesen Seiten übernommen wurden, wurde auf die XSL-Stylesheets übertragen. Der Vorteil unserer neuen Aufgabenteilung besteht darin, dass die Entwicklung der Stylesheets unabhängig von der des Servlets erfolgt, sobald das XML-Format der Servlet-Antworten festgelegt ist.

6.3. Parsen eines XML-Dokuments in Java

Die Versionen 7 und 8 unserer impots-Anwendung werden Clients sein, die für das vorherige xmlsimulations-Servlet programmiert wurden. Diese Clients erhalten XML-Code, den sie parsen müssen, um die benötigten Informationen zu extrahieren. Wir werden nun eine Pause von unseren verschiedenen Versionen einlegen, um zu lernen, wie man ein XML-Dokument in Java parst. Dazu verwenden wir ein in JBuilder 7 enthaltenes Beispiel namens MySaxParser. Das Programm wird wie folgt aufgerufen:

dos>java MySaxParser
Usage: java MySaxParser [URI]

Die Anwendung „MySaxParser“ akzeptiert einen Parameter: die URI (Uniform Resource Identifier) des zu parsenen XML-Dokuments. In unserem Beispiel ist diese URI einfach der Name einer XML-Datei, die sich im Anwendungsverzeichnis von „MySaxParser“ befindet. Betrachten wir zwei Ausführungsbeispiele. Im ersten Beispiel ist die zu parsende XML-Datei „errors.xml“:

<?xml version="1.0" encoding="ISO-8859-1"?>
<?xml-stylesheet type="text/xsl" href="erreurs.xsl"?>
<erreurs>
    <erreur>erreur 1</erreur>
  <erreur>erreur 2</erreur>
</erreurs>

Die Analyse liefert folgende Ergebnisse:

dos> java MySaxParser erreurs.xml
Début du document
Début élément <erreurs>
Début élément <erreur>
[erreur 1]
Fin élément <erreur>
Début élément <erreur>
[erreur 2]
Fin élément <erreur>
Fin élément <erreurs>
Fin du document

Wir haben noch nicht erklärt, was die Anwendung MySaxParser macht, aber hier sehen wir, dass sie die Struktur des geparsten XML-Dokuments anzeigt. Das zweite Beispiel parst die XML-Datei simulations.xml:

<?xml version="1.0" encoding="ISO-8859-1"?>
<?xml-stylesheet type="text/xsl" href="simulations.xsl"?>
<simulations>
  <simulation marie="oui" enfants="2" salaire="200000" impot="22504"/>
  <simulation marie="non" enfants="2" salaire="200000" impot="33388"/>
</simulations>

Die Analyse liefert folgende Ergebnisse:

dos>java MySaxParser simulations.xml
Début du document
Début élément <simulations>
Début élément <simulation>
marie = oui
enfants = 2
salaire = 200000
impot = 22504
Fin élément <simulation>
Début élément <simulation>
marie = non
enfants = 2
salaire = 200000
impot = 33388
Fin élément <simulation>
Fin élément <simulations>
Fin du document

Die Klasse MySaxParser enthält alles, was wir für unsere Steueranwendung benötigen, da sie sowohl die Fehler als auch die Simulationen abrufen konnte, die der Webserver möglicherweise sendet. Sehen wir uns den Code an:

import java.io.IOException;
import org.xml.sax.*;
import org.xml.sax.helpers.*;
import org.apache.xerces.parsers.SAXParser;
import java.util.regex.*;

// the class
public class MySaxParser extends DefaultHandler {

     // value of a tree element XML
    private StringBuffer valeur=new StringBuffer();
    // a regular expression of an element's value when you want to ignore
     // the "blanks" that precede or follow it
    private static Pattern ptnValeur=null;
    private static Matcher résultats=null;

    // -------- hand
    public static void main(String[] argv) {
        // check number of parameters
        if (argv.length != 1) {
            System.out.println("Usage: java MySaxParser [URI]");
            System.exit(0);
        }
         // retrieve the URI from the XML file to be analyzed
        String uri = argv[0];
        try {
            // creation of a XML analyzer (parser)
            XMLReader parser = XMLReaderFactory.createXMLReader("org.apache.xerces.parsers.SAXParser");
             // we indicate to the parser the object that will implement the methods
             // startDocument, endDocument, startElement, endElement, characters
            MySaxParser MySaxParserInstance = new MySaxParser();
            parser.setContentHandler(MySaxParserInstance);
             // initialize an element's value model
            ptnValeur=Pattern.compile("^\\s*(.*?)\\s*$");
             // we indicate to the parser the XML document to be analyzed
            parser.parse(uri);
        }
        catch(Exception ex) {
             // error
            System.err.println("Erreur : " + ex);
             // trace
            ex.printStackTrace();
        }
    }//hand


     // -------- startDocument
    public void startDocument() throws SAXException {
         // procedure called when the parser encounters the start of the document
        System.out.println("Début du document");
    }//startDocument

     // -------- endDocument
    public void endDocument() throws SAXException {
         // procedure called when the parser reaches the end of the document

        System.out.println("Fin du document");
    }//endDocument

     // -------- startElement
    public void startElement(String uri, String localName, String qName,
        Attributes attributes) throws SAXException {
         // procedure called by the parser when it encounters the start of a tag
         // uri: URI of the analyzed document?
         // localName: name of element being analyzed
         // qName: ditto, but "qualified" by a namespace if there is one
         // attributes: list of element attributes

         // follow-up
        System.out.println("Début élément <"+localName+">");
         // does the element have attributes?
        for (int i = 0; i < attributes.getLength(); i++) {
            System.out.println(attributes.getLocalName(i) + " = " + attributes.getValue(i));
        }//for
    }//startElement

    // -------- characters
    public void characters(char[] ch, int start, int length) throws SAXException {
        // procedure called repeatedly by the parser when it encounters text
         // between two <tag>text</tag> tags
         // the text is in ch from the start character on length characters

         // the text is added to the value buffer
        valeur.append(ch, start, length);
    }//characters

        // -------- endElement
    public void endElement(String uri, String localName, String qName)
            throws SAXException {
         // procedure called by the parser when it encounters an end of tag
         // uri: URI of the analyzed document?
         // localName: name of element being analyzed
         // qName: ditto, but "qualified" by a namespace if there is one

         // the value of the
        String strValeur=valeur.toString();
        if (ptnValeur==null) System.out.println("null");
        résultats=ptnValeur.matcher(strValeur);
        if (résultats.find() && ! résultats.group(1).equals("")){
            System.out.println("["+résultats.group(1)+"]");
        }//if
         // set element value to empty
        valeur.setLength(0);

         // follow-up
        System.out.println("Fin élément <"+localName+">");
    }//endElement

}//class

Zunächst wollen wir ein Akronym definieren, das bei der Analyse von XML-Dokumenten häufig vorkommt: SAX, was für „Simple API for XML“ steht. Es handelt sich um eine Reihe von Java-Klassen, die die Arbeit mit XML-Dokumenten erleichtern. Es gibt zwei Versionen der API: SAX1 und SAX2. Die obige Anwendung verwendet die SAX2-API.

Die Anwendung importiert eine Reihe von Paketen:

import org.xml.sax.*;
import org.xml.sax.helpers.*;
import org.apache.xerces.parsers.SAXParser;

Die ersten beiden sind im Lieferumfang von JDK 1.4 enthalten, das dritte jedoch nicht. Das Paket xerces.jar ist auf der Website des Apache Web Servers verfügbar. Es ist sowohl in JBuilder 7 als auch in Tomcat 4.x enthalten:

Image

Wenn Sie also die vorherige Anwendung außerhalb von JBuilder 7 kompilieren möchten und über JDK 1.4 sowie Tomcat 4.x verfügen, können Sie Folgendes schreiben:

dos>javac -classpath ".;E:\Program Files\Apache Tomcat 4.0\common\lib\xerces.jar" MySaxParser.java

Gehen Sie beim Ausführen der Anwendung genauso vor:

dos>java -classpath ".;E:\Program Files\Apache Tomcat 4.0\common\lib\xerces.jar" MySaxParser simulations.xml

Die Klasse MySaxParser erweitert die Klasse DefaultHandler. Darauf kommen wir später zurück. Sehen wir uns den Code für die Hauptprozedur an:

         // retrieve the URI from the XML file to be analyzed
        String uri = argv[0];
        try {
            // creation of a XML analyzer (parser)
            XMLReader parser = XMLReaderFactory.createXMLReader("org.apache.xerces.parsers.SAXParser");
             // we indicate to the parser the object that will implement the methods
             // startDocument, endDocument, startElement, endElement, characters
            MySaxParser MySaxParserInstance = new MySaxParser();
            parser.setContentHandler(MySaxParserInstance);
             // initialize an element's value model
            ptnValeur=Pattern.compile("^\\s*(.*?)\\s*$");
             // we indicate to the parser the XML document to be analyzed
            parser.parse(uri);
        }
        catch(Exception ex) {
             // error
            System.err.println("Erreur : " + ex);
             // trace
            ex.printStackTrace();
        }

Um ein XML-Dokument zu analysieren, benötigt unsere Anwendung einen XML-Parser.

            XMLReader parser = XMLReaderFactory.createXMLReader("org.apache.xerces.parsers.SAXParser");

Der verwendete XML-Parser ist derjenige, der im Paket xerces.jar bereitgestellt wird. Das zurückgegebene Objekt ist vom Typ XMLReader. XMLReader ist eine Schnittstelle, und wir verwenden hier zwei ihrer Methoden:

void setContentHandler(ContentHandler handler)
teilt dem Parser mit, welches ContentHandler-Objekt die Ereignisse verarbeiten soll, die er beim Parsen des XML-Dokuments generiert
void parse(InputSource input)
startet das Parsen des als Parameter übergebenen XML-Dokuments

Wenn der Parser das XML-Dokument analysiert, gibt er Ereignisse aus wie: „Ich bin auf den Anfang des Dokuments, den Anfang eines Tags, ein Tag-Attribut, den Inhalt eines Tags, das Ende eines Tags, das Ende des Dokuments gestoßen, ...“. Er leitet diese Ereignisse an das ihm bereitgestellte ContentHandler-Objekt weiter. ContentHandler ist eine Schnittstelle, die die Methoden definiert, die implementiert werden müssen, um alle Ereignisse zu verarbeiten, die der XML-Parser generieren kann. DefaultHandler ist eine Klasse, die eine Standardimplementierung dieser Methoden bereitstellt. Die in DefaultHandler implementierten Methoden führen keine Aktion aus, sind aber vorhanden. Wenn wir dem Parser mitteilen müssen, welches Objekt die von ihm generierten Ereignisse verarbeiten soll, verwenden wir die Anweisung

void setContentHandler(ContentHandler handler)

mitteilen müssen, ist es praktisch, ein Objekt vom Typ DefaultHandler als Parameter zu übergeben. Würden wir es dabei belassen, würden keine Parser-Ereignisse verarbeitet, aber unser Programm wäre syntaktisch korrekt. In der Praxis übergeben wir dem Parser ein von der DefaultHandler-Klasse abgeleitetes Objekt als Parameter, in dem die Methoden, die nur die für uns interessanten Ereignisse verarbeiten, neu definiert sind. Genau das geschieht hier:

             // we indicate to the parser the object that will implement the methods
             // startDocument, endDocument, startElement, endElement, characters
            MySaxParser MySaxParserInstance = new MySaxParser();
            parser.setContentHandler(MySaxParserInstance);
             // we indicate to the parser the XML document to be analyzed
            parser.parse(uri);

Wir übergeben dem Parser eine Instanz der Klasse mySaxParser, bei der es sich um unsere Klasse handelt, die zuvor durch die Deklaration definiert wurde

public class MySaxParser extends DefaultHandler {

und beginnen mit dem Parsen des Dokuments, dessen URI als Parameter übergeben wurde. Von dort aus beginnt das Parsen des XML-Dokuments. Der Parser gibt Ereignisse aus und ruft für jedes einzelne eine bestimmte Methode des Objekts auf, das für die Verarbeitung dieser Ereignisse zuständig ist – in diesem Fall unser MySaxParser-Objekt. Dieses Objekt verarbeitet fünf bestimmte Ereignisse; die anderen werden ignoriert:

vom Parser ausgelöstes Ereignis
Behandlungsmethode
Beginn des Dokuments
void startDocument()
Ende des Dokuments
void endDocument()
Start eines Elements: <tag>
public void startElement(String uri, String localName, String qName, Attributes attributes)
uri: ?
localName: Name des geparsten Elements. Wenn das gefundene Element <simulations> ist, lautet localName „simulations“.
qName: Namespace-qualifizierter Name des geparsten Elements. Ein XML-Dokument kann einen Namespace definieren, z. B. XX. Der qualifizierte Name des vorangehenden Tags wäre dann XX:simulations.
attributes: Liste der Attribute des Tags
Wert eines Elements:
<Tag>Wert</Tag>
public void characters(char[] ch, int start, int length)
ch: Zeichenarray
start: Index des ersten Zeichens, das im ch-Array verwendet werden soll
length: Anzahl der Zeichen, die aus dem ch-Array entnommen werden sollen
Die Methode characters kann wiederholt aufgerufen werden. Um den Wert eines Elements zu bilden, verwenden wir einen Puffer, den wir:
  • anfangs leer ist
  • bei jedem neuen Aufruf der Methode „characters“ füllen
  • am Ende des Elements beenden
Ende eines Elements: </tag> oder <tag .../>
void endElement(String uri, String localName, String qName)
Die Parameter entsprechen denen der Methode startElement.

Mit der Methode startElement können Sie die Attribute des Elements über den Parameter attributes vom Typ Attributes abrufen:

  • Die Anzahl der Attribute ist über attributes.getLength() abrufbar
  • Der Name des Attributs i ist in attributes.getLocalName(i) verfügbar
  • Der Wert des Attributs i ist in attributes.getValue(i) verfügbar
  • Der Wert des Attributs „localName“ ist in attributes.getValue(localName) verfügbar

Nach dieser Erläuterung sind das vorherige Programm und seine Ausführungsbeispiele selbsterklärend. Es wurde ein regulärer Ausdruck verwendet, um die Werte der Elemente abzurufen, sodass ein XML-Text wie:

<erreur>
    erreur 1
</erreur>

gibt den Text „Fehler 1“ als Wert für das <error>-Tag zurück, wobei alle vorangehenden und/oder nachfolgenden Leerzeichen und Zeilenumbrüche entfernt werden.

6.4. Steuerberechnungsanwendung: Version 7

Wir verfügen nun über alle Elemente, um Clients für unseren Steuerdienst zu schreiben, der XML liefert. Wir werden Version 4 unserer Anwendung für den Client verwenden und Version 6 für den Server beibehalten. In dieser Client-Server-Anwendung:

  • Der Dienst zur Simulation der Steuerberechnung wird vom Servlet „xmlsimulations“ bereitgestellt. Die Antwort des Servers liegt daher im XML-Format vor, wie wir bereits in Version 6 gesehen haben.
  • Der Client ist nicht mehr ein Browser, sondern ein eigenständiger Java-Client. Seine grafische Benutzeroberfläche entspricht der von Version 4.

Hier sind einige Beispiele für die Anwendung in Aktion. Zunächst ein Fehlerszenario: Der Client fragt das xmlsimulations-Servlet ab, obwohl es nicht korrekt initialisiert werden konnte, da das MySQL-DBMS nicht lief:

Image

Wir starten MySQL und führen einige Simulationen durch:

Image

Der Client in dieser neuen Version unterscheidet sich vom Client der Version 4 lediglich darin, wie er die Antwort des Servers verarbeitet. Ansonsten ändert sich nichts. In Version 4 erhielt der Client HTML-Code, aus dem er die benötigten Informationen mithilfe regulärer Ausdrücke extrahierte. Hier erhält der Client XML-Code, aus dem er die benötigten Informationen mithilfe eines XML-Parsers abruft.

Lassen Sie uns die wichtigsten Schritte des Vorgangs im Zusammenhang mit dem Menü „Berechnen“ in Version 4 unseres Clients noch einmal durchgehen, da dort die Änderungen hauptsächlich stattfinden:

  void mnuCalculer_actionPerformed(ActionEvent e) {
....
    try{
             // tax calculation
            calculerImpots(urlImpots,rdOui.isSelected(),nbEnfants.intValue(),salaire);
    }catch (Exception ex){
             // error is displayed
            JOptionPane.showMessageDialog(this,"L'erreur suivante s'est produite : " + ex.getMessage(),"Erreur",JOptionPane.ERROR_MESSAGE);
        }
....
    }//mnuCalculer_actionPerformed

    public void calculerImpots(URL urlImpots,boolean marié, int nbEnfants, int salaire)
          throws Exception{
         // tAX CALCULATION
         // urlImpots : URL of the tax department
         // married: true if married, false otherwise
         // nbEnfants : number of children
         // salary: annual salary

         // remove from urlImpots the info needed to connect to the tax server
....
        try{
             // connect to the server
....

             // create customer input/output flows TCP
....

             // request URL - send HTTP headers
....

             // read the 1st line of the answer
....
             // we read the response through to the end of the headers, looking for any cookies
            while((réponse=IN.readLine())!=null){
....            }//while

            // that's it for HTTP headers - move on to HTML code
             // to retrieve simulations
            ArrayList listeSimulations=getSimulations(IN,OUT,simulations);
            simulations.clear();
            for (int i=0;i<listeSimulations.size();i++){
                simulations.addElement(listeSimulations.get(i));
            }

            // it's over
....
    }//calculerImpots

    private ArrayList getSimulations(BufferedReader IN, PrintWriter OUT, DefaultListModel simulations) throws Exception{
....
    }

Dieser gesamte Code bleibt in der neuen Version gültig. Lediglich die Verarbeitung der HTML-Antwort des Servers (siehe eingerahmter Abschnitt oben) und deren Anzeige müssen durch die Verarbeitung der XML-Antwort des Servers und deren Anzeige ersetzt werden:

             // that's it for HTTP headers - move on to XML code
             // to recover simulations or errors
            ImpotsSaxParser parseur=new ImpotsSaxParser(IN);
            ArrayList listeErreurs=parseur.getErreurs();
            ArrayList listeSimulations=parseur.getSimulations();
             // close server connection
            client.close();

             // display list cleaning
            simulations.clear();
             // errors
            if(listeErreurs.size()!=0){
                 // concatenate all errors
                String msgErreur="Le serveur a signalé les erreurs suivantes :\n";
                for(int i=0;i<listeErreurs.size();i++){
                    msgErreur+=" - "+(String)listeErreurs.get(i);
                }
                 // error display
                throw new Exception(msgErreur);
            }//if

            // simulations
            for (int i=0;i<listeSimulations.size();i++){
                simulations.addElement(listeSimulations.get(i));
            }
            return;

Was bewirkt der obige Codeausschnitt?

  • Es wird ein XML-Parser erstellt und ihm der IN-Stream übergeben, der den vom Server gesendeten XML-Code enthält. Dieser Stream enthielt auch die HTTP-Header, diese wurden jedoch bereits gelesen und verarbeitet. Daher verbleibt nur der XML-Teil der Antwort. Der Parser erzeugt zwei Listen mit Zeichenfolgen: die Liste der Fehler, falls vorhanden, oder die Liste der Simulationen. Diese beiden Listen schließen sich gegenseitig aus.
  • Ist die Liste der Fehler nicht leer, werden die Meldungen in der Liste zu einer einzigen Fehlermeldung verkettet, und es wird eine Ausnahme mit dieser Meldung als Parameter ausgelöst. Diese Ausnahme wird in der Prozedur mnuCalculer_actionPerformed angezeigt, die calculerImpots aufgerufen hat.
  • Ist die Liste der Simulationen nicht leer, wird sie in der jList-Komponente der grafischen Benutzeroberfläche angezeigt.

Betrachten wir nun den Parser für die XML-Antwort des Servers, einen Parser, der direkt auf unserer vorherigen Untersuchung zur Analyse eines XML-Dokuments in Java basiert:

import java.io.IOException;
import org.xml.sax.*;
import org.xml.sax.helpers.*;
import org.apache.xerces.parsers.SAXParser;
import java.util.regex.*;
import java.io.*;
import java.util.*;
import javax.swing.*;

// the class
public class ImpotsSaxParser extends DefaultHandler {

    // value of a tree element XML
    private StringBuffer valeur=new StringBuffer();
    // a regular expression of an element's value when you want to ignore
     // the "blanks" that precede or follow it
    private Pattern ptnValeur=null;
    private Matcher résultats=null;
     // lists of XML elements
    private ArrayList listeSimulations=new ArrayList();
    private ArrayList listeErreurs=new ArrayList();
     // elements XML
    private ArrayList éléments=new ArrayList();
    String élément="";

     // -------- manufacturer
    public ImpotsSaxParser(BufferedReader IN) throws Exception{
        // creation of a XML analyzer (parser)
        XMLReader parser = XMLReaderFactory.createXMLReader("org.apache.xerces.parsers.SAXParser");
         // we indicate to the parser the object that will implement the methods
         // startDocument, endDocument, startElement, endElement, characters
        parser.setContentHandler(this);
        // initialize an element's value model
        ptnValeur=Pattern.compile("^\\s*(.*?)\\s*$");
         // initially no current XML element
        éléments.add("");
         // document analysis
        parser.parse(new InputSource(IN));
    }//manufacturer

     // -------- startElement
    public void startElement(String uri, String localName, String qName,
        Attributes attributes) throws SAXException {
         // procedure called by the parser when it encounters the start of a tag
         // uri: URI of the analyzed document?
         // localName: name of element being analyzed
         // qName: ditto, but "qualified" by a namespace if there is one
         // attributes: list of element attributes

         // note the name of the element
        élément=localName.toLowerCase();
        éléments.add(élément);
         // does the element have attributes?
        if(élément.equals("simulation") && attributes.getLength()==4){
            // it's a simulation - we retrieve the attributes
            String simulation=attributes.getValue("marie")+","+
                                         attributes.getValue("enfants")+","+
                                         attributes.getValue("salaire")+","+
                                         attributes.getValue("impot");
             // add the simulation to the list of simulations
            listeSimulations.add(simulation);
        }//if
    }//startElement

    // -------- characters
    public void characters(char[] ch, int start, int length) throws SAXException {
        // procedure called repeatedly by the parser when it encounters text
         // between two <tag>text</tag> tags
         // the text is in ch from the start character on length characters

         // the text is added to the value buffer if it is the error element
        if (élément.equals("erreur"))
           valeur.append(ch, start, length);
    }//characters

        // -------- endElement
    public void endElement(String uri, String localName, String qName)
            throws SAXException {
         // procedure called by the parser when it encounters an end of tag
         // uri: URI of the analyzed document?
         // localName: name of element being analyzed
         // qName: ditto, but "qualified" by a namespace if there is one

         // case of error
        if(élément.equals("erreur")){
             // retrieve the value of the error element
            String strValeur=valeur.toString();
             // we strip it of its useless "blanks" and register it in the
             // errors if non-empty
            résultats=ptnValeur.matcher(strValeur);
            if (résultats.find() && ! résultats.group(1).equals("")){
                listeErreurs.add(résultats.group(1));
            }//if
        }
         // set element value to empty
        valeur.setLength(0);
         // reset element name
        éléments.remove(éléments.size()-1);
        élément=(String)éléments.get(éléments.size()-1);
    }//endElement

     // --------- getErreurs
    public ArrayList getErreurs(){
        return listeErreurs;
    }

     // --------- getSimulations
    public ArrayList getSimulations(){
        return listeSimulations;
    }

}//class
  • Der Konstruktor empfängt den zu parsten IN-XML-Stream und führt die Parsing-Operation sofort durch. Sobald dies abgeschlossen ist, ist das Objekt konstruiert und die Listen (ArrayList) der Fehler (errorList) und Simulationen (simulationList) wurden erstellt. Für die Prozedur, die das Objekt konstruiert hat, bleibt nur noch, die beiden Listen mithilfe der Methoden getErrors und getSimulations abzurufen.
  • Nur drei vom XML-Parser generierte Ereignisse sind hier von Interesse:
    • der Beginn eines XML-Elements, ein Ereignis, das von der Prozedur startElement behandelt wird. Diese Prozedur behandelt die Tags <simulation marie=".." enfants=".." salaire=".." impot=".."> und <erreur>...</erreur>.
    • Der Wert eines XML-Elements, ein Ereignis, das von der Prozedur characters verarbeitet wird.
    • Das Ende eines XML-Elements, ein Ereignis, das von der Prozedur „endElement“ verarbeitet wird.
  • In der Prozedur startElement rufen wir, wenn es sich um das Element <simulation marie=".." enfants=".." salaire=".." impot=".."> handelt, die vier Attribute mit attributes.getValue("Attributname") ab. In allen Fällen speichern wir den Elementnamen in einer Variablen element und fügen ihn einer Liste (ArrayList) von Elementen hinzu: elem1, elem2, ..., elemN. Diese Liste wird als Stapel verwaltet, wobei das letzte Element das aktuell geparste XML-Element ist. Wenn das Ereignis „Ende des Elements“ eintritt, wird das letzte Element der Liste entfernt und das neue aktuelle Element gesetzt. Dies geschieht in der Prozedur endElement.
  • Die Prozedur characters ist identisch mit der in einem früheren Beispiel behandelten. Wir achten lediglich darauf, zu überprüfen, ob das aktuelle Element tatsächlich das <error>-Element ist – eine Vorsichtsmaßnahme, die hier normalerweise nicht erforderlich ist. Eine solche Vorsichtsmaßnahme wurde auch in der Prozedur startElement getroffen, um zu überprüfen, ob es sich um ein <simulation>-Element handelte.

6.5. Fazit

Dank ihrer XML-Antwort ist die impots-Anwendung sowohl für ihren Entwickler als auch für die Entwickler von Client-Anwendungen einfacher zu verwalten.

  • Die Entwicklung der Serveranwendung kann nun zwei Personengruppen anvertraut werden: dem Java-Entwickler des Servlets und dem Grafikdesigner, der das Erscheinungsbild der Serverantwort in Browsern gestaltet. Letzterer muss lediglich die Struktur der XML-Antwort des Servers kennen, um die dazugehörigen Stylesheets zu erstellen. Beachten Sie, dass diese in separaten XSL-Dateien enthalten sind, die unabhängig vom Java-Servlet sind. Der UI-Designer kann daher unabhängig vom Java-Entwickler arbeiten.
  • Auch die Entwickler von Client-Anwendungen müssen lediglich die Struktur der XML-Antwort des Servers kennen. Etwaige Änderungen, die der Grafikdesigner an den Stylesheets vornimmt, haben keinen Einfluss auf diese XML-Antwort, die stets unverändert bleibt. Dies ist ein enormer Vorteil.
  • Wie kann der Entwickler sein Java-Servlet aktualisieren, ohne dass dabei alles zusammenbricht? Zunächst einmal kann er das Servlet ganz nach Belieben gestalten, solange die XML-Antwort unverändert bleibt. Er kann die XML-Antwort auch aktualisieren, solange er die von seinen Clients erwarteten Elemente <error> und <simulation> beibehält. Er kann dieser Antwort somit neue Tags hinzufügen. Der Frontend-Entwickler berücksichtigt diese in seinen Stylesheets, und Browser können die neuen Versionen der Antwort empfangen. Programmatische Clients funktionieren jedoch weiterhin mit dem alten Modell, da die neuen Tags einfach ignoriert werden. Damit dies funktioniert, müssen die gesuchten Tags bei der XML-Analyse der Serverantwort eindeutig identifiziert werden. Genau das wurde in unserem XML-Client für die Steueranwendung getan, wo in den Verfahren ausdrücklich festgelegt wurde, dass wir die Tags <error> und <simulation> verarbeiten. Infolgedessen werden die anderen Tags ignoriert.