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:

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:

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

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

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.
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:
We zullen later zien wat deze tekst inhoudt. Ten slotte voegt de interpreter de tekst toe:
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:
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
wordt omgezet in de regel HTML:
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:

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:

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:

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:
Hier volgen enkele uitvoervoorbeelden. Het startformulier wordt als volgt ingevuld:

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:

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

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

De browser heeft deze keer de volgende code ontvangen:

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

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:
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.
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:
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 | |
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
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
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 |
void startDocument() | |
void endDocument() | |
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 | |
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:
| |
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:
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:

We starten MySQL en voeren enkele simulaties uit:

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.