Skip to content

6. XML en JAVA

In dit hoofdstuk introduceren we het gebruik van XML-documenten met Java. We doen dit in de context van de belastingtoepassing die in het vorige hoofdstuk is besproken.

6.1. XML-bestanden en XSL-stylesheets

Laten we eens kijken naar het volgende XML simulations.xml-bestand, dat het resultaat zou kunnen zijn van simulaties van belastingberekeningen:

<?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>

Als we dit weergeven met IE 6, krijgen we het volgende resultaat:

Image

IE6 herkent dat het te maken heeft met een bestand XML (dankzij de extensie .xml van het bestand) en zet het op zijn eigen manier op de pagina. Met Netscape krijg je een lege pagina. Als je echter de broncode bekijkt (View/Source), zie je wel degelijk het oorspronkelijke bestand XML:

Image

Waarom geeft Netscape niets weer? Omdat het een stylesheet nodig heeft die aangeeft hoe het bestand XML moet worden omgezet in het bestand HTML, dat het vervolgens wel kan weergeven. Het blijkt dat IE 6 wel een standaardstylesheet heeft, terwijl het bestand XML er geen heeft, wat hier het geval was.

Er bestaat een taal genaamd XSL (eXtended StyleSheet Language) waarmee de transformaties kunnen worden beschreven die nodig zijn om een XML-bestand om te zetten in een willekeurig tekstbestand. XSL maakt het gebruik van talrijke instructies mogelijk en lijkt sterk op programmeertalen. We zullen hier niet in detail op ingaan, omdat dat tientallen pagina’s zou vergen. We zullen slechts twee voorbeelden van XSL-stylesheets beschrijven. De eerste is de stylesheet die het XML-bestand omzet in simulations.xml-code. We passen dit laatste bestand aan zodat het verwijst naar het stylesheet dat browsers kunnen gebruiken om het om te zetten in het document HTML, een document dat ze kunnen weergeven:

<?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>

De opdracht XML

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

wijst het bestand simulations.xsl aan als een stylesheet (xml-stylesheet) van het type text/xsl c.a.d. een tekstbestand dat code bevat XSL. Dit stylesheet wordt door browsers gebruikt om de tekst XML om te zetten in een document HTML. Hieronder ziet u het resultaat dat wordt verkregen met Netscape 7 bij het laden van het bestand XML simulations.xml:

Image

Wanneer we de broncode van het document bekijken (View/Source), zien we het oorspronkelijke document XML terug en niet het weergegeven document HTML:

Image

Netscape heeft het stylesheet simulations.xsl gebruikt om het bovenstaande document XML om te zetten in het weergegeven document HTML. Het is nu tijd om de inhoud van dit stylesheet te bekijken:

<?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>
  • Een stylesheet XSL is een XML-bestand en volgt dus de regels daarvan. Het moet onder andere "goed gevormd" zijn, dat wil zeggen dat elke geopende tag gesloten moet worden.
  • Het bestand begint met twee commando’s XML die in elk stylesheet XSL kunnen worden behouden:
<?xml version="1.0" encoding="ISO-8859-1"?>
  <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

Met het attribuut encoding="ISO-8859-1" kunnen tekens met accenten in het stylesheet worden gebruikt.

  • De tag <xsl:output method="html" indent="yes"/> geeft aan de XSL-interpreter aan dat men "ingesprongen" HTML wil genereren.
  • De tag <xsl:template match="element"> dient om het element van het XML-document te definiëren waarop de instructies tussen <xsl:template ...> en </xsl:template> van toepassing zijn.
    <xsl:template match="/">
................
        </xsl:template>

In het bovenstaande voorbeeld duidt het element "/" de root van het document aan. Dit betekent dat zodra het begin van het document XML wordt aangetroffen, de opdrachten XSL die zich tussen de twee tags bevinden, worden uitgevoerd.

  • Alles wat geen tag XSL is, wordt ongewijzigd in de uitvoerstroom geplaatst. De tags XSL worden uitgevoerd. Sommige daarvan leveren een resultaat op dat in de uitvoerstroom wordt geplaatst. Laten we het volgende voorbeeld eens bekijken:
    <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>

Ter herinnering: het geanalyseerde document XML is het volgende:

<?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>

Vanaf het begin van het geanalyseerde document XML (match="/") zal de interpreter XSL de volgende tekst uitvoeren

<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>

Merk op dat in de oorspronkelijke tekst <hr/> stond en niet <hr>. In de oorspronkelijke tekst kon men niet <hr> schrijven, want hoewel dit een geldige HTML-tag is, is het een ongeldige XML-tag. We hebben hier echter te maken met een XML-tekst die „goed gevormd“ moet zijn, c.a.d wat betekent dat elke tag gesloten moet zijn. We schrijven dus <hr/> en omdat we <xsl:output text="html ..."> hebben geschreven, zal de XSL-interpreter de tekst <hr/> omzetten in <hr>. Achter deze tekst volgt vervolgens de tekst die wordt gegenereerd door het commando XSL:

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

We zullen later zien wat deze tekst inhoudt. Ten slotte voegt de interpreter de tekst toe:

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

Het commando <xsl:apply-templates select="/simulations/simulation"/> vraagt om het "template" (sjabloon) van het element /simulations/simulation uit te voeren. Dit wordt uitgevoerd telkens wanneer de interpreter XSL in de geanalyseerde tekst XML een tag <simulation>..</simulations> of <simulation/> tegenkomt binnen een tag <simulations>..</simulations>. Bij het tegenkomen van de tag <simulation> voert de interpreter de instructies van het volgende model uit:

    <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>

Laten we eens kijken naar de volgende regels XML:

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

De regel <simulation ..> komt overeen met het sjabloon van de instructie XSL <xsl:apply-templates select="/simulations/simulation>". De interpreter XSL zal dus proberen de instructies die bij dit sjabloon horen, erop toe te passen. Hij zal het sjabloon <xsl:template match="simulation"> vinden en dit uitvoeren. Ter herinnering: wat geen XSL-opdracht is, wordt door de XSL-interpreter ongewijzigd overgenomen, terwijl de XSL-opdrachten worden vervangen door het resultaat van hun uitvoering. De instructie XSL <xsl:value-of select="@champ"/> wordt dus vervangen door de waarde van het attribuut "champ" van het geanalyseerde knooppunt (in dit geval een knooppunt <simulation>). De analyse van de voorgaande regel XML levert het volgende resultaat op:

XSL
uitvoer
<tr><td>
<tr><td>
<xsl:value-of select="@marie"/>
ja
</td><td>
</td><td>
<xsl:value-of select="@enfants"/>
2
</td><td>
</td><td>
<xsl:value-of select="@salaris"/>
200000
</td><td>
</td><td>
<xsl:value-of select="@belasting"/>
22504
</td></tr>
</td></tr>

In totaal de regel XML

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

wordt omgezet in de regel HTML:

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

Al deze uitleg is nogal beknopt, maar het zou de lezer nu duidelijk moeten zijn dat de volgende tekst 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>

vergezeld van het volgende stylesheet XSL 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>

genereert de volgende tekst: HTML:

<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>

Het bestand XML simulations.xml, samen met het stylesheet simulations.xsl, wordt bij het openen in een recente browser (hier Netscape 7) als volgt weergegeven:

Image

6.2. Belastingapplicatie: versie 6

6.2.1. De bestanden XML en stylesheets XSL van de belastingapplicatie

Laten we teruggaan naar de webapplicatie voor belastingen en deze aanpassen, zodat het antwoord aan de klanten een antwoord in het formaat XML is in plaats van een antwoord in het formaat HTML. Dit antwoord in het formaat XML gaat vergezeld van een stylesheet in het formaat XSL, zodat browsers het kunnen weergeven. In de vorige paragraaf hebben we het volgende gepresenteerd:

  • het bestand simulations.xml, dat het prototype is van een antwoord XML met simulaties van belastingberekeningen
  • het bestand simulations.xsl, dat het stylesheet XSL zal vormen dat bij dit antwoord XML hoort

We moeten ook rekening houden met het geval waarin het antwoord fouten bevat. Het prototype van het antwoord XML is in dit geval het volgende bestand erreurs.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>

Het stylesheet erreurs.xsl waarmee dit document XML in een browser kan worden weergegeven, ziet er als volgt uit:

<?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>    

Dit stylesheet introduceert een nog niet eerder aangetroffen XSL-opdracht: <xsl:value-of select="."/>. Deze opdracht genereert de waarde van het geanalyseerde knooppunt, in dit geval een <erreur>texte</erreur>-knooppunt. De waarde van dit knooppunt is de tekst tussen de twee openings- en sluitingstags, in dit geval texte.

De code erreurs.xml wordt door het stylesheet erreurs.xsl omgezet in het volgende document HTML:

<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>

Het bestand erreurs.xml wordt samen met het bijbehorende stylesheet door een browser als volgt weergegeven:

Image

6.2.2. De servlet xmlsimulations

We maken een bestand index.html aan en plaatsen dit in de map van de applicatie impots. De weergegeven pagina ziet er als volgt uit:

Image

Dit document HTML is een statisch document. De code ervan is als volgt:

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

      function calculer(){
           // controle van de parameters voordat ze naar de server worden verzonden
        with(document.frmImpots){
          //aantal kinderen
          champs=/^\s*(\d+)\s*$/.exec(txtEnfants.value);
          if(champs==null){
            // het formulier is niet gecontroleerd
            alert("Le nombre d'enfants n'a pas été donné ou est incorrect");
            nbEnfants.focus();
            return;
          }//if
          //salaris
          champs=/^\s*(\d+)\s*$/.exec(txtSalaire.value);
          if(champs==null){
            // het model is niet gecontroleerd
            alert("Le salaire n'a pas été donné ou est incorrect");
            salaire.focus();
            return;
          }//als
          // oké – verzenden
          submit();
        }//met
      }//berekenen        
        </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>

Merk op dat de gegevens uit het formulier worden verzonden naar URL /impots/xmlsimulations. Deze applicatie is een Java-servlet die als volgt is geconfigureerd in het bestand web.xml van de applicatie impots:

<?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>
  • De servlet heet xmlsimulations en is gebaseerd op de klasse xmlsimulations.class.
  • De servlet heeft als parameters de parameters DSNimpots, admimpots en mdpimpots, die nodig zijn om toegang te krijgen tot de belastingdatabase. Daarnaast ondersteunt de servlet nog twee andere parameters:
    • xslSimulations, de naam van het stijlbestand dat bij het antwoord XML met de simulaties moet worden gevoegd
    • xslErreurs, de naam van het stijlbestand dat bij het antwoord XML moet worden gevoegd, dat eventuele fouten bevat
  • het heeft een alias xmlsimulations, waardoor het toegankelijk is via URL http://localhost:8080/impots/xmlsimulations.

De structuur van de servlet xmlsimulations is vergelijkbaar met die van de reeds besproken servlet simulations. Het belangrijkste verschil is dat deze XML moet genereren in plaats van HTML. Dit zal leiden tot het verwijderen van de JSP-bestanden die in de vorige toepassingen werden gebruikt. Hun belangrijkste functie was het verbeteren van de leesbaarheid van de gegenereerde HTML-code, door te voorkomen dat deze zou verdwijnen in de Java-code van de servlet. Deze functie is nu niet meer nodig. De servlet moet twee soorten XML-code genereren:

  • die voor de simulaties
  • die voor fouten

We hebben eerder de twee soorten XML-antwoorden die in deze twee gevallen moeten worden gegeven, evenals de bijbehorende stylesheets, gepresenteerd en bestudeerd. De code van de servlet is als volgt:

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

public class xmlsimulations extends HttpServlet{

    // instantievariabelen
    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{

        // we halen de schrijfstroom naar de client op
        PrintWriter out=response.getWriter();

        // het type van het antwoord specificeren
        response.setContentType("text/xml");

         // de lijst met fouten
        ArrayList erreurs=new ArrayList();

         // is de initialisatie goed verlopen?
        if(msgErreur!=null){
             // het is klaar – het antwoord met fouten wordt naar de server verzonden
            erreurs.add(msgErreur);
            sendErreurs(out,xslErreurs,erreurs);
             // het is klaar
            return;
        }

         // we halen de eerdere simulaties van de sessie op
        HttpSession session=request.getSession();
        ArrayList simulations=(ArrayList)session.getAttribute("simulations");
        if(simulations==null) simulations=new ArrayList();

        // de parameters van de huidige aanvraag worden opgehaald
        String optMarie=request.getParameter("optMarie");              // burgerlijke staat
        String txtEnfants=request.getParameter("txtEnfants");       // aantal kinderen
        String txtSalaire=request.getParameter("txtSalaire");       // jaarsalaris

         // zijn alle verwachte parameters aanwezig
        if(optMarie==null || txtEnfants==null || txtSalaire==null){
             // er ontbreken parameters
             // het antwoord wordt met fouten verzonden
            erreurs.add("Demande incomplète. Il manque des paramètres");
            sendErreurs(out,xslErreurs,erreurs);
             // het is klaar
            return;
        }

         // we hebben alle parameters – we controleren ze
         // burgerlijke staat
        if( ! optMarie.equals("oui") && ! optMarie.equals("non")){
             // fout
            erreurs.add("Etat marital incorrect");
        }
         // aantal kinderen
        txtEnfants=txtEnfants.trim();
        if(! Pattern.matches("^\\d+$",txtEnfants)){
            // fout
            erreurs.add("Nombre d'enfants incorrect");
        }
         // salaris
        txtSalaire=txtSalaire.trim();
        if(! Pattern.matches("^\\d+$",txtSalaire)){
            // fout
            erreurs.add("Salaire incorrect");
        }

        if(erreurs.size()!=0){
            // als er fouten zijn, worden deze gemeld
            sendErreurs(out,xslErreurs,erreurs);
        }else{
             // geen fouten
            try{
                 // de te betalen belasting kan worden berekend
                int nbEnfants=Integer.parseInt(txtEnfants);
                int salaire=Integer.parseInt(txtSalaire);
                String txtImpots=""+impots.calculer(optMarie.equals("oui"),nbEnfants,salaire);
                 // het huidige resultaat wordt toegevoegd aan de eerdere simulaties
                String[] simulation={optMarie.equals("oui") ? "oui" : "non",txtEnfants, txtSalaire, txtImpots};
                simulations.add(simulation);
                 // het antwoord met simulaties wordt verzonden
                sendSimulations(out,xslSimulations,simulations);
            }catch(Exception ex){}
        }//if-else
         // de lijst met simulaties wordt teruggeplaatst in de sessie
        session.setAttribute("simulations",simulations);
    }//GET

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

     //-------- INIT
    public void init(){
         // de initialisatieparameters worden opgehaald
        ServletConfig config=getServletConfig();
        xslSimulations=config.getInitParameter("xslSimulations");
        xslErreurs=config.getInitParameter("xslErreurs");
        DSNimpots=config.getInitParameter("DSNimpots");
        admimpots=config.getInitParameter("admimpots");
        mdpimpots=config.getInitParameter("mdpimpots");

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

         // we maken een instantie aan van 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";
         // het antwoord wordt verzonden
        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++){
            // simulatie nr. 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";
        }//voor
        réponse+="</simulations>\n";
         // het antwoord wordt verzonden
        out.println(réponse);
    }
}

Laten we de belangrijkste nieuwigheden van deze code eens nader bekijken in vergelijking met wat we al kenden:

  • de init-procedure haalt nieuwe parameters op uit het configuratiebestand web.xml: de namen van de twee stylesheets XSL die bij het antwoord moeten worden gevoegd, worden in de variabelen xslSimulations en xslErreurs geplaatst. Deze twee stylesheets zijn de eerder besproken bestanden simulations.xsl en erreurs.xsl. Deze worden in de map van de applicatie impots geplaatst:
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
  • De procedure GET controleert eerst of er een fout is opgetreden tijdens de initialisatie. Zo ja, dan roept ze de procedure sendErreurs aan, die het voor dit geval geschikte antwoord XML genereert en vervolgens stopt. In dit antwoord wordt de instructie ingevoegd die aangeeft welk stylesheet moet worden gebruikt.
  • Als er geen fouten zijn opgetreden, analyseert de procedure GET de parameters van het verzoek van de klant. Als er een fout wordt gevonden, meldt de procedure dit, eveneens met behulp van de procedure sendErreurs. Anders berekent de procedure de nieuwe simulatie, voegt deze toe aan de eerdere simulaties die in de huidige sessie zijn opgeslagen en sluit af door het antwoord XML te verzenden via de procedure sendSimulations. Deze laatste procedure werkt op dezelfde manier als de procedure sendErreurs.
  • Opgemerkt moet worden dat de servlet zijn antwoord aangeeft als zijnde van het type text/xml:
         // het type antwoord wordt gespecificeerd
        response.setContentType("text/xml");

Hier volgen enkele uitvoervoorbeelden. Het startformulier wordt als volgt ingevuld:

Image

De database MySQL is niet gestart, waardoor het onmogelijk is om het object impots aan te maken in de init-procedure van de servlet. Het antwoord van de servlet is dan als volgt:

Image

De code die de browser ontvangt (View/Source) is als volgt:

Image

Als we nu twee simulaties opnieuw uitvoeren nadat we de database MySQL hebben gestart, krijgen we het volgende resultaat:

Image

De browser heeft deze keer de volgende code ontvangen:

Image

We merken op dat onze nieuwe toepassing eenvoudiger is dan voorheen, doordat de bestanden JSP zijn verwijderd. Een deel van het werk dat door deze pagina’s werd gedaan, is overgeheveld naar de stylesheets XSL. Het voordeel van onze nieuwe taakverdeling is dat, zodra het formaat XML van de antwoorden van de servlet is vastgelegd, de ontwikkeling van de stylesheets onafhankelijk is van die van de servlet.

6.3. Analyse van een XML-document in Java

De versies 7 en 8 van onze belastingapplicatie zullen geprogrammeerde clients zijn van de vorige servlet xmlsimulations. Deze zullen XML-code ontvangen die ze moeten analyseren om de informatie eruit te halen die voor hen van belang is. We zullen hier even pauzeren bij onze verschillende versies en leren hoe we een XML-document in Java kunnen analyseren. We doen dit aan de hand van een voorbeeld dat bij JBuilder 7 wordt meegeleverd, genaamd MySaxParser. Het programma heeft de volgende naam:

dos>java MySaxParser
Usage: java MySaxParser [URI]

De toepassing MySaxParser accepteert één parameter: de URI (Uniform Resource Identifier) van het te analyseren document XML. In ons voorbeeld is deze URI simpelweg de naam van een XML-bestand dat zich in de map van de applicatie MySaxParser bevindt. Laten we twee uitvoervoorbeelden bekijken. In het eerste voorbeeld is het geanalyseerde bestand XML het bestand erreurs.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>

De analyse levert de volgende resultaten op:

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

We hadden nog niet vermeld wat de toepassing MySaxParser doet, maar hier zien we dat deze de structuur weergeeft van het geanalyseerde document XML. Het tweede voorbeeld analyseert het bestand XML 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>

De analyse levert de volgende resultaten op:

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

De klasse MySaxParser bevat alles wat we nodig hebben in onze belastingapplicatie, aangezien deze zowel de fouten als de simulaties heeft kunnen opvangen die de webserver zou kunnen versturen. Laten we de code eens bekijken:

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

// de klasse
public class MySaxParser extends DefaultHandler {

     // waarde van een element in de boom XML
    private StringBuffer valeur=new StringBuffer();
    // een reguliere expressie voor de waarde van een element wanneer men dit wil negeren
     // de "spaties" ervoor of erna
    private static Pattern ptnValeur=null;
    private static Matcher résultats=null;

    // -------- main
    public static void main(String[] argv) {
        // controle van het aantal parameters
        if (argv.length != 1) {
            System.out.println("Usage: java MySaxParser [URI]");
            System.exit(0);
        }
         // we halen de URI op uit het te analyseren bestand XML
        String uri = argv[0];
        try {
            // aanmaken van een XML-parser
            XMLReader parser = XMLReaderFactory.createXMLReader("org.apache.xerces.parsers.SAXParser");
             // we geven aan de parser aan welk object de methoden gaat implementeren
             // startDocument, endDocument, startElement, endElement, tekens
            MySaxParser MySaxParserInstance = new MySaxParser();
            parser.setContentHandler(MySaxParserInstance);
             // wordt de waardesjabloon van een element geïnitialiseerd
            ptnValeur=Pattern.compile("^\\s*(.*?)\\s*$");
             // het document XML wordt aan de parser doorgegeven om te analyseren
            parser.parse(uri);
        }
        catch(Exception ex) {
             // fout
            System.err.println("Erreur : " + ex);
             // logboek
            ex.printStackTrace();
        }
    }//main


     // -------- startDocument
    public void startDocument() throws SAXException {
         // procedure die wordt aangeroepen wanneer de parser het begin van het document tegenkomt
        System.out.println("Début du document");
    }//startDocument

     // -------- endDocument
    public void endDocument() throws SAXException {
         // procedure die wordt aangeroepen wanneer de parser het einde van het document bereikt

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

     // -------- startElement
    public void startElement(String uri, String localName, String qName,
        Attributes attributes) throws SAXException {
         // procedure die door de parser wordt aangeroepen wanneer deze het begin van een tag tegenkomt
         // uri: URI van het geanalyseerde document?
         // localName: naam van het element dat momenteel wordt geanalyseerd
         // qName: idem, maar ‘gekwalificeerd’ door een naamruimte indien aanwezig
         // attributes: lijst met attributen van het element

         // volging
        System.out.println("Début élément <"+localName+">");
         // heeft het element attributen?
        for (int i = 0; i < attributes.getLength(); i++) {
            System.out.println(attributes.getLocalName(i) + " = " + attributes.getValue(i));
        }//voor
    }//startElement

    // -------- tekens
    public void characters(char[] ch, int start, int length) throws SAXException {
        // procedure die herhaaldelijk door de parser wordt aangeroepen wanneer deze tekst tegenkomt
         // tussen twee tags <tag>tekst</tag>
         // de tekst staat in ch vanaf het startteken over length tekens

         // de tekst wordt toegevoegd aan de buffer waarde
        valeur.append(ch, start, length);
    }//tekens

        // -------- endElement
    public void endElement(String uri, String localName, String qName)
            throws SAXException {
         // procedure die door de parser wordt aangeroepen wanneer deze een einde-tag tegenkomt
         // URI: URI van het geanalyseerde document?
         // localName: naam van het element dat momenteel wordt geanalyseerd
         // qName: idem, maar ‘gekwalificeerd’ door een naamruimte indien aanwezig

         // de waarde van het element wordt weergegeven
        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
         // wordt de waarde van het element op leeg gezet
        valeur.setLength(0);

         // gevolgd door
        System.out.println("Fin élément <"+localName+">");
    }//endElement

}//klasse

Laten we eerst een afkorting definiëren die vaak voorkomt bij de analyse van XML-documenten: SAX, wat staat voor Simple API for XML. Dit is een verzameling Java-klassen die het werken met XML-documenten vergemakkelijkt. Er zijn twee versies van API: SAX1 en SAX2. De bovenstaande toepassing maakt gebruik van API en SAX2.

De applicatie importeert een aantal pakketten:

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

De eerste twee worden meegeleverd met JDK 1.4, de derde niet. Het pakket xerces.jar is beschikbaar op de website van de Apache-webserver. Het wordt geleverd met JBuilder 7, maar ook met Tomcat 4.x:

Image

Als men dus de vorige applicatie buiten JBuilder 7 om wil compileren en men beschikt over JDK 1.4 en Tomcat 4.x, dan kan men het volgende schrijven:

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

Bij het uitvoeren doe je hetzelfde:

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

De klasse MySaxParser is afgeleid van de klasse DefaultHandler. We komen hier later op terug. Laten we de code van de procedure main eens bekijken:

         // we halen de URI op uit het te analyseren bestand XML
        String uri = argv[0];
        try {
            // aanmaken van een XML-parser
            XMLReader parser = XMLReaderFactory.createXMLReader("org.apache.xerces.parsers.SAXParser");
             // we geven aan de parser aan welk object de methoden gaat implementeren
             // startDocument, endDocument, startElement, endElement, tekens
            MySaxParser MySaxParserInstance = new MySaxParser();
            parser.setContentHandler(MySaxParserInstance);
             // wordt de waardesjabloon van een element geïnitialiseerd
            ptnValeur=Pattern.compile("^\\s*(.*?)\\s*$");
             // het document XML wordt aan de parser doorgegeven om te analyseren
            parser.parse(uri);
        }
        catch(Exception ex) {
             // fout
            System.err.println("Erreur : " + ex);
             // logbericht
            ex.printStackTrace();
        }

Om een document met de code XML te analyseren, heeft onze applicatie een XML-code-analysator nodig, ook wel 'parser' genoemd.

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

De gebruikte parser XML is afkomstig uit het pakket xerces.jar. Het opgehaalde object is van het type XMLReader. XMLReader is een interface waarvan we hier twee methoden gebruiken:

void setContentHandler(ContentHandler handler)
geeft de parser het object van het type ContentHandler aan dat de gebeurtenissen zal verwerken die de parser genereert tijdens de analyse van het document XML
void parse(InputSource input)
start de analyse van het document XML dat als parameter is doorgegeven

Wanneer de parser het document XML gaat analyseren, zal hij gebeurtenissen genereren zoals: "ik ben het begin van het document tegengekomen, het begin van een tag, een tagattribuut, de inhoud van een tag, het einde van een tag, het einde van het document, ...". Hij geeft deze gebeurtenissen door aan het object ContentHandler dat aan hem is doorgegeven. ContentHandler is een interface die de methoden definieert die moeten worden geïmplementeerd om alle gebeurtenissen te verwerken die de parser XML kan genereren. DefaultHandler is een klasse die een standaardimplementatie van deze methoden biedt. De methoden die in DefaultHandler zijn geïmplementeerd, doen niets, maar ze zijn wel aanwezig. Wanneer aan de parser moet worden aangegeven welk object de gebeurtenissen zal verwerken die hij zal genereren, gebeurt dit met behulp van de instructie

void setContentHandler(ContentHandler handler)

is het handig om een object van het type DefaultHandler als parameter door te geven. Als we het daarbij zouden laten, zou geen enkele gebeurtenis van de parser worden verwerkt, maar zou ons programma wel syntactisch correct zijn. In de praktijk geven we als parameter aan de parser een object door dat is afgeleid van de klasse DefaultHandler, waarin de methoden zijn geherdefinieerd die uitsluitend de gebeurtenissen verwerken waarin we geïnteresseerd zijn. Dat is wat hier gebeurt:

             // het object dat de methoden gaat implementeren wordt aan de parser doorgegeven
             // startDocument, endDocument, startElement, endElement, tekens
            MySaxParser MySaxParserInstance = new MySaxParser();
            parser.setContentHandler(MySaxParserInstance);
             // de parser wordt geïnstrueerd om het document XML te analyseren
            parser.parse(uri);

We geven een exemplaar van de klasse mySaxParser door aan de parser. Dit is onze klasse, die hierboven is gedefinieerd met de declaratie

public class MySaxParser extends DefaultHandler {

en starten we de analyse van het document waarvan we de URI als parameter hebben doorgegeven. Vanaf dat moment begint de analyse van het document XML. De parser genereert gebeurtenissen en roept voor elk daarvan een specifieke methode aan van het object dat verantwoordelijk is voor de verwerking van deze gebeurtenissen, in dit geval ons object MySaxParser. Dit object verwerkt vijf specifieke gebeurtenissen; de overige worden genegeerd:

gebeurtenis gegenereerd door de parser
verwerkingsmethode
début du document    
void startDocument()
fin du document
void endDocument()
début d'un élément :<balise>
public void startElement(String uri, String localName, String qName, Attributes attributes)
uri: ?
localName: naam van het geanalyseerde element. Als het aangetroffen element <simulations> is, is localName="simulations".
qName: de met een naamruimte gekwalificeerde naam van het geanalyseerde element. Een document XML kan een naamruimte definiëren, bijvoorbeeld XX. De gekwalificeerde naam van de voorgaande tag zou dan XX:simulations zijn.
attributes: lijst met attributen van de tag
valeur d'un élément :
<balise>valeur</balise>
public void characters(char[] ch, int start, int length)
ch: tekenreeks
start: index van het eerste teken dat in de array ch moet worden gebruikt
length: aantal tekens dat uit de array ch moet worden gehaald
De methode characters kan herhaaldelijk worden aangeroepen. Om de waarde van een element op te bouwen, wordt dan een buffer gebruikt die:
  • aan het begin van het element leeg is
  • bij elke nieuwe aanroep van de methode characters aanvult
  • aan het einde van het element wordt afgesloten
fin d'un élément : </balise> ou <balise .../>
void endElement(String uri, String localName, String qName)
de parameters zijn dezelfde als die van de methode startElement.

Met de methode startElement kun je de attributen van het element ophalen via de parameter attributes van het type Attributes:

  • het aantal attributen is beschikbaar in attributes.getLength()
  • de naam van het attribuut i is beschikbaar in attributes.getLocalName(i)
  • de waarde van het attribuut i is beschikbaar in attributes.getValue(i)
  • de waarde van het attribuut met de naam localName in attributes.getValue(localName)

Nu dit is uitgelegd, spreekt het voorgaande programma, samen met de uitvoervoorbeelden, voor zich. Er is een reguliere expressie gebruikt om de waarden van de elementen op te halen, zodat een tekst XML zoals:

<erreur>
    erreur 1
</erreur>

de bij de tag <erreur> behorende waarde ‘fout 1’ oplevert, ontdaan van spaties en regeleinden die eraan voorafgaan en/of erop volgen.

6.4. Belastingapplicatie: versie 7

We beschikken nu over alle elementen om clients te schrijven die zijn geprogrammeerd voor onze belastingdienst, die XML levert. We gebruiken versie 4 van onze applicatie om de client te maken en behouden versie 6 voor de server. In deze client-server-applicatie:

  • wordt de simulatieservice voor de belastingberekening uitgevoerd door de servlet xmlsimulations. Het antwoord van de server is dus in het formaat XML, zoals we in versie 6 hebben gezien.
  • is de client niet langer een browser, maar een zelfstandige Java-client. De grafische interface is die van versie 4.

Hier volgen enkele voorbeelden van de uitvoering. Allereerst een foutgeval: de client vraagt de servlet xmlsimulations op terwijl deze niet correct kon worden geïnitialiseerd omdat de SGBD MySQL niet was gestart:

Image

We starten MySQL en voeren enkele simulaties uit:

Image

De client van deze nieuwe versie verschilt alleen van de client van versie 4 in de manier waarop hij het antwoord van de server verwerkt. Verder verandert er niets. In versie 4 ontving de client de code HTML, waaruit hij met behulp van reguliere expressies de informatie haalde die voor hem van belang was. Hier ontvangt de client de code XML, waaruit hij de informatie die hem interesseert ophaalt met behulp van een parser XML.

Laten we de hoofdlijnen van de procedure met betrekking tot het menu ‘Berekenen’ van versie 4 van onze client nog eens op een rijtje zetten, aangezien de wijzigingen voornamelijk daar plaatsvinden:

  void mnuCalculer_actionPerformed(ActionEvent e) {
....
    try{
             // de belasting wordt berekend
            calculerImpots(urlImpots,rdOui.isSelected(),nbEnfants.intValue(),salaire);
    }catch (Exception ex){
             // de fout wordt weergegeven
            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{
         // belastingberekening
         // urlImpots: URL van de belastingdienst
         // getrouwd: true indien getrouwd, false anders
         // nbEnfants: aantal kinderen
         // salaris: jaarsalaris

         // uit urlImpots worden de gegevens gehaald die nodig zijn om verbinding te maken met de belastingdienstserver
....
        try{
             // er wordt verbinding gemaakt met de server
....

             // de in- en uitgaande stromen van de klant TCP worden aangemaakt
....

             // de URL wordt opgevraagd - verzending van de headers HTTP
....

             // de eerste regel van het antwoord wordt gelezen
....
             // het antwoord wordt tot het einde van de headers gelezen, op zoek naar een eventuele cookie
            while((réponse=IN.readLine())!=null){
....            }//while

            // de headers zijn afgelopen HTTP - we gaan verder met de code HTML
             // om de simulaties op te halen
            ArrayList listeSimulations=getSimulations(IN,OUT,simulations);
            simulations.clear();
            for (int i=0;i<listeSimulations.size();i++){
                simulations.addElement(listeSimulations.get(i));
            }

            // dat is klaar
....
    }//calculerImpots

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

Al deze code blijft geldig in de nieuwe versie. Alleen de verwerking van het serverantwoord HTML (hierboven in het kader) en de weergave ervan moeten worden vervangen door de verwerking van het serverantwoord XML en de weergave ervan:

             // dat was het voor de kopteksten HTTP - we gaan verder met de code XML
             // om de simulaties of fouten op te halen
            ImpotsSaxParser parseur=new ImpotsSaxParser(IN);
            ArrayList listeErreurs=parseur.getErreurs();
            ArrayList listeSimulations=parseur.getSimulations();
             // verbinding met de server verbreken
            client.close();

             // opschonen van de weergavelijst
            simulations.clear();
             // fouten
            if(listeErreurs.size()!=0){
                 // alle fouten samenvoegen
                String msgErreur="Le serveur a signalé les erreurs suivantes :\n";
                for(int i=0;i<listeErreurs.size();i++){
                    msgErreur+=" - "+(String)listeErreurs.get(i);
                }
                 // fouten weergeven
                throw new Exception(msgErreur);
            }//if

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

Wat doet het bovenstaande stukje code?

  • Het maakt een parser XML aan en geeft deze de stream IN door, die de door de server verzonden code XML bevat. Deze stream bevatte ook de headers HTTP, maar deze zijn al gelezen en verwerkt. Er blijft dus alleen het gedeelte XML van het antwoord over. De parser genereert twee lijsten met tekenreeksen: de lijst met fouten, indien er fouten zijn opgetreden, of anders de lijst met simulaties. Deze twee lijsten sluiten elkaar uit.
  • Als de lijst met fouten niet leeg is, worden de berichten in de lijst samengevoegd tot één foutbericht en wordt er een uitzondering gegenereerd met dit bericht als parameter. Deze uitzondering wordt weergegeven in de procedure mnuCalculer_actionPeformed die calculerImpots heeft aangeroepen.
  • Als de lijst met simulaties niet leeg is, wordt deze weergegeven in de component jList van de grafische interface.

Laten we nu eens kijken naar de parser voor het antwoord XML van de server, een parser die rechtstreeks voortvloeit uit de studie die we eerder hebben uitgevoerd over het analyseren van een XML-document in Java:

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.*;

// de klasse
public class ImpotsSaxParser extends DefaultHandler {

    // waarde van een element in de boom XML
    private StringBuffer valeur=new StringBuffer();
    // een reguliere expressie voor de waarde van een element wanneer men
     // de "spaties" die ervoor of erna staan
    private Pattern ptnValeur=null;
    private Matcher résultats=null;
     // de lijsten met elementen XML
    private ArrayList listeSimulations=new ArrayList();
    private ArrayList listeErreurs=new ArrayList();
     // onderdelen XML
    private ArrayList éléments=new ArrayList();
    String élément="";

     // -------- fabrikant
    public ImpotsSaxParser(BufferedReader IN) throws Exception{
        // aanmaken van een XML-parser
        XMLReader parser = XMLReaderFactory.createXMLReader("org.apache.xerces.parsers.SAXParser");
         // we geven aan de parser aan welk object de methoden gaat implementeren
         // startDocument, endDocument, startElement, endElement, tekens
        parser.setContentHandler(this);
        // het waardemodel van een element wordt geïnitialiseerd
        ptnValeur=Pattern.compile("^\\s*(.*?)\\s*$");
         // in het begin is er geen actueel element XML
        éléments.add("");
         // het document wordt geanalyseerd
        parser.parse(new InputSource(IN));
    }//constructor

     // -------- startElement
    public void startElement(String uri, String localName, String qName,
        Attributes attributes) throws SAXException {
         // procedure die door de parser wordt aangeroepen wanneer deze het begin van een tag tegenkomt
         // uri: URI van het geanalyseerde document?
         // localName: naam van het element dat momenteel wordt geanalyseerd
         // qName: idem, maar gekwalificeerd door een naamruimte indien aanwezig
         // attributes: lijst met attributen van het element

         // de naam van het element wordt vermeld
        élément=localName.toLowerCase();
        éléments.add(élément);
         // heeft het element attributen?
        if(élément.equals("simulation") && attributes.getLength()==4){
            // het is een simulatie  we halen de attributen op
            String simulation=attributes.getValue("marie")+","+
                                         attributes.getValue("enfants")+","+
                                         attributes.getValue("salaire")+","+
                                         attributes.getValue("impot");
             // de simulatie wordt toegevoegd aan de lijst met simulaties
            listeSimulations.add(simulation);
        }//if
    }//startElement

    // -------- tekens
    public void characters(char[] ch, int start, int length) throws SAXException {
        // procedure die herhaaldelijk door de parser wordt aangeroepen wanneer deze tekst tegenkomt
         // tussen twee tags <tag>tekst</tag>
         // de tekst bevindt zich in ch vanaf het startteken over length tekens

         // de tekst wordt toegevoegd aan de buffer waarde s als het om het element fout gaat
        if (élément.equals("erreur"))
           valeur.append(ch, start, length);
    }//tekens

        // -------- endElement
    public void endElement(String uri, String localName, String qName)
            throws SAXException {
         // procedure die door de parser wordt aangeroepen wanneer deze een einde-tag tegenkomt
         // uri: URI van het geanalyseerde document?
         // localName: naam van het element dat momenteel wordt geanalyseerd
         // qName: idem, maar gekwalificeerd door een naamruimte indien aanwezig

         // in geval van een fout
        if(élément.equals("erreur")){
             // we halen de waarde van het foutelement op
            String strValeur=valeur.toString();
             // we verwijderen de overbodige "spaties" en slaan de waarde op in de lijst met
             // fouten, als deze niet leeg is
            résultats=ptnValeur.matcher(strValeur);
            if (résultats.find() && ! résultats.group(1).equals("")){
                listeErreurs.add(résultats.group(1));
            }//if
        }
         // wordt de waarde van het element op leeg gezet
        valeur.setLength(0);
         // de naam van het element wordt gereset
        é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;
    }

}//klasse
  • De constructor ontvangt de te analyseren stream XML IN en voert deze analyse onmiddellijk uit. Zodra deze analyse is voltooid, is het object aangemaakt en zijn de lijsten (ArrayList) met fouten (listeErreurs) en simulaties (listeSimulations) samengesteld. Het enige wat de procedure die het object heeft aangemaakt nu nog hoeft te doen, is de twee lijsten ophalen met behulp van de methoden getErreurs en getSimulations.
  • Slechts drie gebeurtenissen die door de parser XML worden gegenereerd, zijn hier voor ons van belang:
    • het begin van een element XML, een gebeurtenis die wordt verwerkt door de procedure startElement. Deze procedure moet de tags <simulation marie=".." enfants=".." salaire=".." impot=".."> en <erreur>...</erreur> verwerken.
    • waarde van een element XML, een gebeurtenis die wordt verwerkt door de procedure characters.
    • einde van een element XML, een gebeurtenis die wordt verwerkt door de procedure endElement.
  • in de procedure startElement, als het gaat om het element <simulation marie=".." enfants=".." salaire=".." impot="..">, worden de vier attributen opgehaald via attributes.getValue("naam van het attribuut"). In alle gevallen wordt de naam van het element opgeslagen in een variabele ‘element’ en toegevoegd aan een lijst (ArrayList) met elementen: el1, el2, …, eln. Deze lijst wordt beheerd als een stapel waarvan het laatste element het element XML is dat momenteel wordt geanalyseerd. Bij de gebeurtenis "einde element" wordt het laatste element uit de lijst verwijderd en wordt het nieuwe huidige element aangepast. Dit gebeurt in de procedure endElement.
  • De procedure `characters` is identiek aan die welke in een eerder voorbeeld werd besproken. We controleren alleen even of het huidige element inderdaad het element <erreur> is, een voorzorgsmaatregel die hier normaal gesproken niet nodig is. Een soortgelijke voorzorgsmaatregel was ook genomen in de procedure startElement om te controleren of het om een element <simulation> ging.

6.5. Conclusie

Dankzij het antwoord XML is de impots-applicatie eenvoudiger te beheren geworden, zowel voor de ontwerper ervan als voor de ontwerpers van de clientapplicaties.

  • Het ontwerp van de serverapplicatie kan nu worden toevertrouwd aan twee soorten mensen: de Java-ontwikkelaar van de servlet en de grafisch ontwerper die het uiterlijk van het serverantwoord in de browsers zal beheren. Deze laatste hoeft alleen de structuur van het XML-antwoord van de server te kennen om de bijbehorende stylesheets te kunnen opstellen. Ter herinnering: deze worden opgeslagen in aparte XSL-bestanden die onafhankelijk zijn van de Java-servlet. De grafisch ontwerper kan dus onafhankelijk van de Java-ontwikkelaar werken.
  • Ook de ontwerpers van de clienttoepassingen hoeven alleen maar de structuur van het XML-antwoord van de server te kennen. De wijzigingen die de grafisch ontwerper in de stylesheets aanbrengt, hebben geen invloed op dit XML-antwoord, dat altijd hetzelfde blijft. Dit is een enorm voordeel.
  • Hoe kan de ontwikkelaar zijn Java-servlet verder ontwikkelen zonder alles kapot te maken? Ten eerste kan hij, zolang zijn antwoord XML niet verandert, zijn servlet naar eigen inzicht inrichten. Hij kan ook het antwoord XML aanpassen, zolang hij de elementen <fout> en <simulatie> behoudt die zijn clients verwachten. Zo kan hij nieuwe tags aan dit antwoord toevoegen. De grafisch ontwerper houdt hier rekening mee in zijn stylesheets en de browsers kunnen de nieuwe versies van het antwoord weergeven. De geprogrammeerde clients blijven echter werken met het oude model, waarbij de nieuwe tags simpelweg worden genegeerd. Om dit mogelijk te maken, moeten de gezochte tags in de analyse XML van het serverantwoord correct worden geïdentificeerd. Dit is gedaan in onze client XML van de belastingapplicatie, waar in de procedures specifiek werd aangegeven dat de tags <erreur> en <simulation> werden verwerkt. Daardoor worden de andere tags genegeerd.