13. Voorbeeld 10 – Conversie en validatie van reële getallen
De nieuwe applicatie biedt de mogelijkheid om reële getallen in te voeren:
![]() |
- in [1], het invoerformulier
- in [2], het teruggestuurde antwoord
De applicatie werkt op dezelfde manier als bij de invoer van gehele getallen, dus zullen we alleen de punten bespreken die verschillen.
13.1. Het NetBeans-project
Het NetBeans-project is als volgt:
![]() |
- in [1], de weergaven van de applicatie
- [Accueil.JSP]: de startpagina
- [FormDouble.JSP]: het invoerformulier
- [ConfirmationDouble.JSP]: de bevestigingspagina
- in [2], het berichtenbestand [messages.properties] en het hoofdconfiguratiebestand van Struts
- in [3]:
- [FormDouble.java]: de actie die het formulier weergeeft en verwerkt
- [FormDouble-validation.xml]: de validatieregels voor de actie [FormDouble]. Dit bestand delegeert deze validaties aan het model volgens de methode die zojuist is besproken.
- [FormDoubleModel]: het model van de actie [FormDouble]
- [FormDoubleModel-validation.xml]: de validatieregels van het model
- [FormDoubleModel.properties]: het berichtenbestand van het model
- [example.xml]: secundair configuratiebestand van Struts
13.2. De configuratie van het project
Het project wordt voornamelijk geconfigureerd via het volgende bestand [example.xml]:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
"http://struts.apache.org/dtds/struts-2.0.dtd">
<struts>
<package name="example" namespace="/example" extends="struts-default">
<action name="Accueil">
<result name="success">/example/Accueil.JSP</result>
</action>
<action name="FormDouble" class="example.FormDouble">
<result name="input">/example/FormDouble.JSP</result>
<result name="cancel" type="redirect">/example/Accueil.JSP</result>
<result name="success">/example/ConfirmationFormDouble.JSP</result>
</action>
</package>
</struts>
Dit is vergelijkbaar met het bestand dat is besproken voor de invoer van gehele getallen.
13.3. De berichtenbestanden
Het bestand [messages.properties] ziet er als volgt uit:
Accueil.titre=Accueil
Accueil.message=Struts 2 - Conversions et validations
Accueil.FormDouble=Saisie de nombres r\u00e9els
Form.titre=Conversions et validations
FormDouble.message=Struts 2 - Conversion et validation de nombres r\u00e9els
FormDouble.conseil=Tapez les nombres r\u00e9els avec une virgule comme 10,7
Form.submitText=Valider
Form.cancelText=Annuler
Form.clearModel=Raz mod\u00e8le
Confirmation.titre=Confirmation
Confirmation.message=Confirmation des valeurs saisies
Confirmation.champ=champ
Confirmation.valeur=valeur
Confirmation.lien=Formulaire de test
xwork.default.invalid.fieldvalue=Valeur invalide pour le champ "{0}".
Het bestand [FormDoubleModel.properties] ziet er als volgt uit:
double.format={0,number}
double1.prompt=1-Nombre r\u00E9el
double1.error=Tapez un nombre r\u00E9el
double2.prompt=2-Nombre r\u00E9el
double2.error=Tapez un nombre r\u00E9el
double3.prompt=3-Nombre r\u00E9el >=2.64
double3.error=Tapez un nombre r\u00E9el >=2.64
double4.prompt=4-Nombre r\u00E9el <8.32
double4.error=Tapez un nombre r\u00E9el <8.32
double5.prompt=5-Nombre r\u00E9el dans l''intervalle [2.64,8.32[
double5.error=Tapez un nombre r\u00E9el dans l''intervalle [2.64,8.32[
double6.prompt=6-Nombre r\u00E9el dans l''intervalle [2.64,8.32]
double6.error=Tapez un nombre r\u00E9el dans l''intervalle [2.64,8.32]
Regel 1 speelt een belangrijke rol. We komen hier later op terug.
13.4. Het invoerformulier
De weergave [FormDouble.JSP] ziet er als volgt uit:
<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="s" uri="/struts-tags" %>
<html>
<head>
<title><s:text name="Form.titre"/></title>
<s:head/>
</head>
<body background="<s:url value="/ressources/standard.jpg"/>">
<h2><s:text name="FormDouble.message"/></h2>
<h4><s:text name="FormDouble.conseil"/></h4>
<s:form name="formulaire" action="FormDouble">
<s:textfield name="double1" key="double1.prompt"/>
<s:textfield name="double2" key="double2.prompt" value="%{#parameters['double2']!=null ? #parameters['double2'] : double2==null ? '' :getText('double.format',{double2})}"/>
<s:textfield name="double3" key="double3.prompt" value="%{#parameters['double3']!=null ? #parameters['double3'] : double3==null ? '' :getText('double.format',{double3})}"/>
<s:textfield name="double4" key="double4.prompt" value="%{#parameters['double4']!=null ? #parameters['double4'] : double4==null ? '' :getText('double.format',{double4})}"/>
<s:textfield name="double5" key="double5.prompt" value="%{#parameters['double5']!=null ? #parameters['double5'] : double5==null ? '' :getText('double.format',{double5})}"/>
<s:textfield name="double6" key="double6.prompt"/>
<s:submit key="Form.submitText" method="execute"/>
</s:form>
<br/>
<s:url id="URL" action="FormDouble" method="cancel"/>
<s:a href="%{URL}"><s:text name="Form.cancelText"/></s:a>
<br/>
<s:url id="URL" action="FormDouble" method="clearModel"/>
<s:a href="%{URL}"><s:text name="Form.clearModel"/></s:a>
</body>
</html>
De regels 13 tot en met 18 zijn de zes invoervelden voor reële getallen. De velden double2 tot en met double5 hebben een complex attribuut value. Normaal gesproken zouden de zes invoervelden er als volgt uit moeten zien:
<s:textfield name="double1" key="double1.prompt"/>
<s:textfield name="double2" key="double2.prompt"/>
<s:textfield name="double3" key="double3.prompt"/>
<s:textfield name="double4" key="double4.prompt"/>
<s:textfield name="double5" key="double5.prompt"/>
<s:textfield name="double6" key="double6.prompt"/>
Om bepaalde problemen op te lossen die tijdens het testen zijn opgetreden, moesten we de zaken wat ingewikkelder maken. Voorlopig kan de lezer deze complexiteit negeren. We zullen dit later uitleggen.
13.5. Het bevestigingsscherm
Het bevestigingsscherm [ConfirmationFormDouble.JSP] ziet er als volgt uit:
![]() |
De code ervan is als volgt:
<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="s" uri="/struts-tags" %>
<html>
<head>
<title><s:text name="Confirmation.titre"/></title>
<s:head/>
</head>
<body background="<s:url value="/ressources/standard.jpg"/>">
<h2><s:text name="Confirmation.message"/></h2>
<table border="1">
<tr>
<th><s:text name="Confirmation.champ"/></th>
<th><s:text name="Confirmation.valeur"/></th>
</tr>
<tr>
<td><s:text name="double1.prompt"/></td>
<td><s:text name="double1"/></td>
</tr>
<tr>
<td><s:text name="double2.prompt"/></td>
<td>
<s:text name="double.format">
<s:param value="double2"/>
</s:text>
</td>
</tr>
<tr>
<td><s:text name="double3.prompt"/></td>
<td>
<s:text name="double.format">
<s:param value="double3"/>
</s:text>
</td>
</tr>
<tr>
<td><s:text name="double4.prompt"/></td>
<td>
<s:text name="double.format">
<s:param value="double4"/>
</s:text>
</td>
</tr>
<tr>
<td><s:text name="double5.prompt"/></td>
<td>
<s:text name="double.format">
<s:param value="double5"/>
</s:text>
</td>
</tr>
<tr>
<td><s:text name="double6.prompt"/></td>
<td><s:text name="double6"/></td>
</tr>
</table>
<br/>
<s:url id="URL" action="FormDouble!input"/>
<s:a href="%{URL}"><s:text name="Confirmation.lien"/></s:a>
</body>
</html>
Het scherm [ConfirmationFormDouble.JSP] geeft alleen het sjabloon [FormDoubleModel] weer. De regels 47-49 tonen de weergave van een reëel getal. We willen dat deze weergave rekening houdt met de localisation van de applicatie. Volgens dit model wordt een reëel getal in Frankrijk (10,7) anders weergegeven dan in Groot-Brittannië (10.7). Hiervoor gebruiken we de tag <s:text>, die we tot nu toe hadden gebruikt voor de internationalisering van de applicatie. Deze tag dient dus ook voor de lokalisatie.
Hier wordt de berichtcode double.format gebruikt. Deze code is te vinden in het bestand [FormDoubleModel.properties]:
double.format={0,number}
De waarde die aan de sleutel is gekoppeld, is hier geen bericht, maar een weergaveformaat:
- 0 is een parameter die de weer te geven waarde vertegenwoordigt. In dit geval is dat het veld double5 van het sjabloon.
- number staat voor het getalformaat. Dit wordt aangepast aan elk land. Zonder dit formaat wordt het getal 10,7 altijd weergegeven als 10,7, ongeacht het land.
De tag
<s:text name="double.format">
<s:param value="double5"/>
</s:text>
Zorgt ervoor dat het bericht {0, number} wordt weergegeven, waarbij de parameter double5 (regel 2) de parameter 0 van het formaat vervangt. Zo wordt het sjabloon double5 weergegeven in het lokale numerieke formaat.
13.6. Het sjabloon [FormDoubleModel]
De velden double1 tot en met double6 van het formulier [FormDouble.JSP] worden ingevoegd in het volgende sjabloon [FormDoubleModel]:
package example;
public class FormDoubleModel {
// constructor zonder parameters
public FormDoubleModel() {
}
// velden
private String double1;
private Double double2;
private Double double3;
private Double double4;
private Double double5;
private String double6;
// model-raz
public void clearModel() {
double1 = null;
double2 = null;
double3 = null;
double4 = null;
double5 = null;
double6 = null;
}
// getters en setters
...
}
- de velden double1 en double6 zijn van het type String
- de overige velden zijn van het type Double
13.7. De validatie van het model
De validatie van het model wordt gecontroleerd door twee bestanden: [FormDouble-validation.xml] en [FormDoubleModel-validation.xml].
Het bestand [FormDouble-validation.xml] delegeert de validaties aan [FormDoubleModel-validation.xml]:
<!--
<!DOCTYPE validators PUBLIC "-//OpenSymphony Group//XWork Validator 1.0.2//
EN" "http://www.opensymphony.com/xwork/xwork-validator-1.0.2.dtd">
-->
<!DOCTYPE validators PUBLIC "-//OpenSymphony Group//XWork Validator 1.0.2//
EN" "http://localhost:8084/voorbeeld-10/example/xwork-validator-1.0.2.dtd">
<validators>
<field name="model" >
<field-validator type="visitor">
<param name="appendPrefix">false</param>
<message/>
</field-validator>
</field>
</validators>
We zijn dit bestand al eerder tegengekomen.
Het bestand [FormDoubleModel-validation.xml] bevat de volgende validatieregels:
<!--
<!DOCTYPE validators PUBLIC "-//OpenSymphony Group//XWork Validator 1.0.2//
EN" "http://www.opensymphony.com/xwork/xwork-validator-1.0.2.dtd">
-->
<!DOCTYPE validators PUBLIC "-//OpenSymphony Group//XWork Validator 1.0.2//
EN" "http://localhost:8084/voorbeeld-10/example/xwork-validator-1.0.2.dtd">
<validators>
<field name="double1" >
<field-validator type="requiredstring" short-circuit="true">
<message key="double1.error"/>
</field-validator>
<field-validator type="regex" short-circuit="true">
<param name="expression">^[+|-]*\s*\d+(,\d+)*$</param>
<param name="trim">true</param>
<message key="double1.error"/>
</field-validator>
</field>
<field name="double2" >
<field-validator type="required" short-circuit="true">
<message key="double2.error"/>
</field-validator>
<field-validator type="conversion" short-circuit="true">
<message key="double2.error"/>
</field-validator>
</field>
<field name="double3" >
<field-validator type="required" short-circuit="true">
<message key="double3.error"/>
</field-validator>
<field-validator type="conversion" short-circuit="true">
<message key="double3.error"/>
</field-validator>
<field-validator type="double" short-circuit="true">
<param name="minInclusive">2.64</param>
<message key="double3.error"/>
</field-validator>
</field>
<field name="double4" >
<field-validator type="required" short-circuit="true">
<message key="double4.error"/>
</field-validator>
<field-validator type="conversion" short-circuit="true">
<message key="double4.error"/>
</field-validator>
<field-validator type="double" short-circuit="true">
<param name="maxExclusive">8.32</param>
<message key="double4.error"/>
</field-validator>
</field>
<field name="double5" >
<field-validator type="required" short-circuit="true">
<message key="double5.error"/>
</field-validator>
<field-validator type="conversion" short-circuit="true">
<message key="double5.error"/>
</field-validator>
<field-validator type="double" short-circuit="true">
<param name="minInclusive">2.64</param>
<param name="maxExclusive">8.32</param>
<message key="double5.error"/>
</field-validator>
</field>
<field name="double6" >
<field-validator type="requiredstring" short-circuit="true">
<message key="double6.error"/>
</field-validator>
<field-validator type="regex" short-circuit="true">
<param name="expression">^[+|-]*\s*\d+(,\d+)*$</param>
<param name="trim">true</param>
<message key="double6.error"/>
</field-validator>
</field>
</validators>
- de regel op de regels 12-21 controleert of het invoerveld double1 voldoet aan het patroon van een reguliere expressie die een reëel getal vertegenwoordigt.
- De regel op de regels 23-30 controleert of het invoerveld double2 kan worden geconverteerd naar een reëel getal van het type double.
- De regel in de regels 32-43 controleert of het invoerveld double3 kan worden omgezet in een reëel getal van het type double >=2,64. Let op: er moet de Angelsaksische notatie voor reële getallen worden gebruikt.
- De regel in de regels 45-56 controleert of het invoerveld double4 kan worden geconverteerd naar een double-getal <8,32.
- De regel in de regels 58-70 controleert of het invoerveld double5 kan worden geconverteerd naar een double-getal in het interval [2,64; 8,32[.
- De regel in de regels 72-80 controleert of het veld double6 voldoet aan het patroon van een reguliere expressie die een reëel getal vertegenwoordigt.
Zodra het bestand [FormDoubleModel-validation.xml] door de validatie-interceptor is verwerkt, laat deze de methode validate van de actie [FormDouble] uitvoeren, indien deze bestaat. We zullen deze samen met de volledige actie presenteren.
13.8. De actie [FormDouble]
De actie [FormDouble] is als volgt:
package example;
import com.opensymphony.xwork2.ActionSupport;
import com.opensymphony.xwork2.ModelDriven;
import java.util.Map;
import org.apache.struts2.interceptor.SessionAware;
import org.apache.struts2.interceptor.validation.SkipValidation;
public class FormDouble extends ActionSupport implements ModelDriven, SessionAware {
// constructor zonder parameters
public FormDouble() {
}
// actiemodel
public Object getModel() {
if (session.get("model") == null) {
session.put("model", new FormDoubleModel());
}
return session.get("model");
}
@SkipValidation
public String clearModel() {
// reset van het model
((FormDoubleModel) getModel()).clearModel();
// resultaat
return INPUT;
}
public String cancel() {
// het model wordt opgeschoond
((FormDoubleModel) getModel()).clearModel();
// resultaat
return "cancel";
}
// SessionAware
Map<String, Object> session;
public void setSession(Map<String, Object> session) {
this.session = session;
}
// validatie
@Override
public void validate() {
// dubbele invoer6 geldig?
if (getFieldErrors().get("double6") == null) {
// de komma wordt vervangen door een punt in de tekenreeks double6
String strDouble6 = (((FormDoubleModel) getModel()).getDouble6()).replace(',', '.');
// String --> double
double double6 = Double.parseDouble(strDouble6);
// controle
if (double6 < 2.64 || double6 > 8.32) {
addFieldError("double6", getText("double6.error"));
}
}
}
}
De actie [FormDouble] is opgebouwd volgens hetzelfde model als de actie [FormInt]. We zullen alleen de methode validate toelichten. Ter herinnering: de methode validate wordt uitgevoerd na verwerking van het validatiebestand [FormDoubleModel-validation.xml] en vóór de uitvoering van de methode execute.
- regel 48: als er al fouten zijn in het veld double6, wordt er verder niets gedaan.
- regel 50: er is een tekenreeks in de vorm 45,67 ontvangen. Deze is opgeslagen in het veld double6 van het model. De komma wordt vervangen door een punt, zodat 45.67 ontstaat.
- regel 52: de tekenreeks 45.67 wordt omgezet in een dubbel reëel getal. Dit moet lukken, aangezien de tekenreeks double6 voldoet aan het formaat van een reëel getal.
- regel 54: er wordt gecontroleerd of het verkregen dubbele reële getal binnen het interval [2.64, 8.32] ligt.
- regel 55: als dat niet het geval is, wordt de foutmelding met sleutel double6.error toegevoegd aan het veld double6. Deze melding is te vinden in het bestand [FormDoubleModel.properties]. Ze wordt weergegeven wanneer het foutieve formulier opnieuw wordt getoond.
13.9. Laatste details
Laten we nu teruggaan naar de complexiteit van de invoervelden van het formulier [FormDouble.JSP]. We gaan het veld double2 als voorbeeld nemen. Deze redenering geldt ook voor de velden double3 tot en met double5, die een sjabloon van het type Double hebben. Voor de velden double1 en double6, die een sjabloon van het type String hebben, is er geen probleem.
Het invoerveld double2 is als volgt:
<s:textfield name="double2" key="double2.prompt" value="%{#parameters['double2']!=null ? #parameters['double2'] : double2==null ? '' :getText('double.format',{double2})}"/>
Laten we beginnen met de eenvoudigste tag:
en laten we eens kijken wat er gebeurt:
![]() |
- in [1] wordt het juiste getal double2 gevalideerd. Op de schermafbeelding is het niet heel goed te zien, maar we hebben het getal met een komma ingevoerd.
- in [2], het bevestigingsscherm. De invoer double2 is door de validatietests gekomen. Het getal wordt weergegeven met een komma.
![]() |
- in [3] keren we terug naar het formulier
- in [4], het formulier. Wat op de schermafbeelding niet goed te zien is, is dat het getal double2, dat aanvankelijk 4,32 was, nu 4.32 is geworden met een decimaalteken.
![]() |
- in [5], we valideren het formulier opnieuw zonder iets te wijzigen
- in [6] wordt er een fout gemeld in het veld double2.
Het probleem is het volgende:
- aanvankelijk werd in [1] de invoerreeks „4,32“ met succes omgezet in het reële getal 4,32. Dit betekent dat de bewerking String → Double is geslaagd en dat Struts in dit opzicht dus rekening houdt met de locale, in dit geval Frankrijk.
- Het nieuwe formulier [4] geeft in het veld double2 de waarde van het reële getal 4.32 weer. Aangezien de weergave niet is gelokaliseerd, wordt deze standaard op de Angelsaksische manier weergegeven, namelijk als c.a.d, met een punt als decimaalteken. Bij de conversie van Double naar String houdt Struts dus geen rekening meer met de locale, anders zou het 4,32 met een komma hebben weergegeven.
Dat is op zijn zachtst gezegd inconsistent. Maar dat geeft niet, we gaan de weergave van het getal 4,32 lokaliseren. De invoertag wordt dan als volgt:
<s:textfield name="double2" key="double2.prompt" value="%{getText('double.format',{double2})}"/>
Het attribuut value specificeert de waarde die moet worden weergegeven in het veld double2. Dit is de waarde van een uitdrukking OGNL. Laten we nog eens kijken naar de definitie van de sleutel double.format in het bestand [FormDoubleModel.properties]:
double.format={0,number}
Met de methode getText('sleutel') kan het bericht worden opgehaald dat bij een sleutel hoort. Dit bericht wordt opgezocht in het bestand dat overeenkomt met de huidige locale. Als de locale bijvoorbeeld 'es' (Spanje) was, zou de sleutel double.format zijn opgezocht in het bestand [FormDoubleModel_es.properties].
Met de methode getText('sleutel', {param0, param1, ...}) kan een bericht met bepaalde parameters worden opgehaald. Het bericht
is een bericht dat is geconfigureerd met parameter 0. Dit is een positionele parameter. De methode getText('double.format', {double2}) wijst het getal double2 toe aan parameter 0. Uiteindelijk wordt de waarde van double2 opgevraagd in het gelokaliseerde numerieke formaat. In Frankrijk wordt het getal 4,56 gelokaliseerd als de tekenreeks "4,56".
Na deze omzetting voeren we de tests opnieuw uit.
![]() |
Al bij de eerste weergave van het formulier treedt er een fout op bij [1]. Laten we teruggaan naar de tag:
<s:textfield name="double2" key="double2.prompt" value="%{getText('double.format',{double2})}"/>
Bij de eerste weergave is de waarde van het model double2 gelijk aan null, een niet-numerieke waarde. We passen de tag als volgt aan:
<s:textfield name="double2" key="double2.prompt" value="%{double2==null ? '' : getText('double.format',{double2})}"/>
Deze keer wordt getest of double2==null. Zo ja, dan wordt een lege tekenreeks weergegeven.
Nadat deze wijziging is doorgevoerd, hervatten we de tests:
![]() |
![]() |
De schermen [1] tot en met [4] laten zien dat het probleem dat we wilden oplossen, is opgelost:
- in [1] voeren we 4,67 in
- in [2] is dit getal geaccepteerd
- in [3] wordt het opnieuw weergegeven als 4,67 met de komma, wat wordt bevestigd door [4].
Helaas houden de problemen daar niet op. Laten we eens naar de volgende reeks kijken:
![]() |
- in [5] voegen we een teken toe om double2
- in [6] hebben de validatietests hun werk gedaan en wordt de fout gemeld. Alleen is de weergegeven tekenreeks in [6] niet de foutieve tekenreeks, maar de huidige waarde van het sjabloon double2. De ingevoerde waarde is verloren gegaan.
Bij het overschakelen van [5] naar [6] wordt de aanvraag niet voltooid. Deze wordt gestopt door de validatie-interceptor. Bij [6] wordt de waarde van het model double2 weergegeven, dat vanwege deze onderbreking geen nieuwe waarde heeft ontvangen. Er wordt dus de vorige waarde weergegeven, terwijl de ingevoerde tekenreeks had moeten worden weergegeven. De parameters van een verzoek zijn in één oogopslag te bekijken via de notatie #parameters['param']. We passen het invoerveld double2 als volgt aan:
<s:textfield name="double2" key="double2.prompt" value="%{#parameters['double2']!=null ? #parameters['double2'] : double2==null ? '' : getText('double.format',{double2})}"/>
De weergegeven waarde van het veld double2 wordt als volgt berekend: als de parameter 'double2' bestaat, wordt deze weergegeven; anders wordt het sjabloon double2 weergegeven. De lezer wordt verzocht te testen of deze nieuwe versie van de tag de ondervonden problemen oplost.
13.10. Conclusion
Het is verbazingwekkend dat het invoeren van reële getallen met validatiecontrole zo ingewikkeld is... Misschien heb ik iets over het hoofd gezien in de documentatie?









