13. Esempio 10 – Conversione e convalida dei numeri reali
La nuova applicazione presenta l’inserimento dei numeri reali:
![]() |
- in [1], il modulo di immissione
- in [2], la risposta restituita
Il funzionamento dell’applicazione è simile a quello dell’inserimento dei numeri interi, pertanto commenteremo solo i punti che differiscono.
13.1. Il progetto NetBeans
Il progetto NetBeans è il seguente:
![]() |
- in [1], le viste dell'applicazione
- [Accueil.JSP]: la pagina iniziale
- [FormDouble.JSP]: il modulo di inserimento dati
- [ConfirmationDouble.JSP]: la pagina di conferma
- in [2], il file dei messaggi [messages.properties] e il file di configurazione principale di Struts
- in [3]:
- [FormDouble.java]: l’azione che visualizza ed elabora il modulo
- [FormDouble-validation.xml]: le regole di convalida dell’azione [FormDouble]. Questo file delega tali convalide al modello secondo il metodo appena illustrato.
- [FormDoubleModel]: il modello dell'azione [FormDouble]
- [FormDoubleModel-validation.xml]: le regole di convalida del modello
- [FormDoubleModel.properties]: il file dei messaggi del modello
- [example.xml]: file di configurazione secondario di Struts
13.2. Configurazione del progetto
Il progetto è configurato principalmente dal seguente file [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>
È analogo a quello esaminato per l’inserimento dei numeri interi.
13.3. I file dei messaggi
Il file [messages.properties] è il seguente:
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}".
Il file [FormDoubleModel.properties] è il seguente:
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]
La riga 1 riveste un ruolo importante. Ci torneremo più avanti.
13.4. Il modulo di inserimento dati
La vista [FormDouble.JSP] è la seguente:
<%@ 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>
Le righe da 13 a 18 sono i sei campi di immissione dei numeri reali. I campi da double2 a double5 hanno un attributo complesso value. Normalmente i sei campi di immissione dovrebbero essere i seguenti:
<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"/>
Per risolvere alcuni problemi riscontrati durante i test, è stato necessario complicare le cose. Per il momento, il lettore può ignorare questa complessità. La spiegheremo in seguito.
13.5. La schermata di conferma
La schermata di conferma [ConfirmationFormDouble.JSP] è la seguente:
![]() |
Il suo codice è il seguente:
<%@ 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>
La vista [ConfirmationFormDouble.JSP] si limita a visualizzare il modello [FormDoubleModel]. Le righe 47-49 mostrano la visualizzazione di un numero reale. Si desidera che tale visualizzazione tenga conto della localisation dell'applicazione. In base a quest’ultimo, un numero reale non verrà visualizzato allo stesso modo in Francia (10,7) e in Gran Bretagna (10.7). A tal fine, si utilizza il tag <s:text> che era stato impiegato fino a quel momento per l’internazionalizzazione dell’applicazione. Questo tag serve quindi anche per la localizzazione.
In questo caso, la chiave del messaggio utilizzata è double.format. Questa chiave si trova nel file [FormDoubleModel.properties]:
double.format={0,number}
Il valore associato alla chiave non è in questo caso un messaggio, ma un formato di visualizzazione:
- 0 è un parametro che rappresenta il valore da visualizzare. In questo caso si tratterà del campo double5 del modello.
- number rappresenta il formato numerico. Verrà adattato a ciascun paese. Senza questo formato, il numero 10,7 verrà sempre visualizzato come 10,7 indipendentemente dal paese.
Il tag
<s:text name="double.format">
<s:param value="double5"/>
</s:text>
fa visualizzare il messaggio {0, number}, dove il parametro double5 (riga 2) sostituirà il parametro 0 del formato. In questo modo, il modello double5 verrà visualizzato nel formato numerico localizzato.
13.6. Il modello [FormDoubleModel]
I campi da double1 a double6 del modulo [FormDouble.JSP] vengono inseriti nel seguente modello [FormDoubleModel]:
package example;
public class FormDoubleModel {
// costruttore senza parametri
public FormDoubleModel() {
}
// campi
private String double1;
private Double double2;
private Double double3;
private Double double4;
private Double double5;
private String double6;
// modello di riempimento
public void clearModel() {
double1 = null;
double2 = null;
double3 = null;
double4 = null;
double5 = null;
double6 = null;
}
// getter e setter
...
}
- i campi double1 e double6 sono di tipo String
- gli altri campi sono di tipo Double
13.7. La convalida del modello
La convalida del modello è controllata da due file: [FormDouble-validation.xml] e [FormDoubleModel-validation.xml].
Il file [FormDouble-validation.xml] delega le validazioni a [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/esempio-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>
Abbiamo già incontrato questo file.
Il file [FormDoubleModel-validation.xml] contiene le seguenti regole di convalida:
<!--
<!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/esempio-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>
- la regola delle righe 12-21 verifica che il campo di immissione double1 segua il modello di un'espressione regolare che rappresenta un numero reale.
- la regola delle righe 23-30 verifica che il campo di immissione double2 possa essere convertito in un numero reale di tipo double.
- la regola delle righe 32-43 verifica che il campo di immissione double3 possa essere convertito in un numero reale di tipo double >=2,64. Si noti che è necessario utilizzare la notazione anglosassone dei numeri reali.
- La regola delle righe 45-56 verifica che il campo di immissione double4 possa essere convertito in un numero reale di tipo double <8,32.
- La regola delle righe 58-70 verifica che il campo di immissione double5 possa essere convertito in un numero reale di tipo double nell'intervallo [2,64; 8,32[.
- La regola delle righe 72-80 verifica che il campo double6 rispetti il modello di un'espressione regolare che rappresenta un numero reale.
Una volta che il file [FormDoubleModel-validation.xml] è stato elaborato dall’intercettatore di convalida, quest’ultimo fa eseguire il metodo validate dell’azione [FormDouble], se presente. Lo presenteremo insieme all’intera azione.
13.8. L’azione [FormDouble]
L’azione [FormDouble] è la seguente:
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 {
// costruttore senza parametri
public FormDouble() {
}
// modello dell'azione
public Object getModel() {
if (session.get("model") == null) {
session.put("model", new FormDoubleModel());
}
return session.get("model");
}
@SkipValidation
public String clearModel() {
// campo vuoto del modello
((FormDoubleModel) getModel()).clearModel();
// risultato
return INPUT;
}
public String cancel() {
// si pulisce il modello
((FormDoubleModel) getModel()).clearModel();
// risultato
return "cancel";
}
// SessionAware
Map<String, Object> session;
public void setSession(Map<String, Object> session) {
this.session = session;
}
// convalida
@Override
public void validate() {
// il doppio inserimento 6 è valido?
if (getFieldErrors().get("double6") == null) {
// si sostituisce la virgola con il punto nella stringa double6
String strDouble6 = (((FormDoubleModel) getModel()).getDouble6()).replace(',', '.');
// Stringa --> double
double double6 = Double.parseDouble(strDouble6);
// verifica
if (double6 < 2.64 || double6 > 8.32) {
addFieldError("double6", getText("double6.error"));
}
}
}
}
L'azione [FormDouble] è costruita sullo stesso modello dell'azione [FormInt]. Commenteremo solo il metodo validate. Si ricorda che il metodo validate viene eseguito dopo l'elaborazione del file di convalida [FormDoubleModel-validation.xml] e prima dell'esecuzione del metodo execute.
- riga 48: se sono già presenti errori nel campo double6, non viene eseguita alcuna ulteriore operazione.
- riga 50: è stata rilevata una stringa della forma 45,67. È stata memorizzata nel campo double6 del modello. Si sostituisce la virgola con il punto per ottenere 45.67.
- riga 52: la stringa 45.67 viene convertita in un numero reale di tipo double. La conversione deve aver funzionato, poiché la stringa double6 rispetta il formato di un numero reale.
- riga 54: si verifica che il numero reale a doppia precisione ottenuto rientri nell’intervallo [2.64, 8.32].
- riga 55: se così non fosse, il messaggio di errore con chiave double6.error viene associato al campo double6. Questo messaggio sarà reperibile nel file [FormDoubleModel.properties]. Verrà visualizzato quando il modulo errato verrà ripresentato.
13.9. Ultimi dettagli
Ora torniamo alla complessità dei campi di inserimento del modulo [FormDouble.JSP]. Prenderemo in esame il campo double2. Il ragionamento si estende ai campi da double3 a double5 che hanno un modello di tipo Double. Per i campi double1 e double6, che hanno un modello di tipo String, non vi è alcun problema.
Il campo di immissione double2 è il seguente:
<s:textfield name="double2" key="double2.prompt" value="%{#parameters['double2']!=null ? #parameters['double2'] : double2==null ? '' :getText('double.format',{double2})}"/>
Partiamo dal tag più semplice:
e vediamo cosa succede:
![]() |
- in [1], viene convalidato il numero corretto double2. Nello screenshot non si vede molto bene, ma il numero è stato digitato con una virgola.
- in [2], la schermata di conferma. L’inserimento double2 ha superato i test di convalida. Il numero viene visualizzato con una virgola.
![]() |
- in [3], si torna al modulo
- in [4], il modulo. Ciò che lo screenshot non mostra chiaramente è che il numero double2, che inizialmente era 4,32, è diventato 4.32 con un punto decimale.
![]() |
- in [5], si convalida nuovamente il modulo senza apportare alcuna modifica
- in [6], viene segnalato un errore nel campo double2.
Il problema è il seguente:
- inizialmente in [1], la stringa inserita «4,32» è stata convertita correttamente nel numero reale 4,32. Ciò significa che l’operazione String → Double ha avuto esito positivo e che, in questo senso, Struts tiene conto delle impostazioni locali, in questo caso della Francia.
- Il nuovo modulo [4] visualizza nel campo double2 il valore del numero reale 4.32. Poiché la visualizzazione non è stata localizzata, viene visualizzata per impostazione predefinita secondo il formato anglosassone, c.a.d, con il punto decimale. Pertanto, nella conversione da Double a String, Struts non tiene più conto delle impostazioni locali, altrimenti avrebbe visualizzato 4,32 con la virgola.
È a dir poco incoerente. Ma non importa, localizzeremo la visualizzazione del numero 4,32. Il tag di inserimento diventa il seguente:
<s:textfield name="double2" key="double2.prompt" value="%{getText('double.format',{double2})}"/>
L’attributo value specifica il valore da visualizzare nel campo double2. Si tratta del valore di un’espressione OGNL. Ricordiamo la definizione della chiave double.format nel file [FormDoubleModel.properties]:
double.format={0,number}
Il metodo getText('chiave') consente di ottenere il messaggio associato a una chiave. Il messaggio viene cercato nel file corrispondente alle impostazioni locali correnti. Pertanto, se la locale fosse «es» (Spagna), la chiave double.format sarebbe stata cercata nel file [FormDoubleModel_es.properties].
Il metodo getText('chiave', {param0, param1, ...}) consente di recuperare un messaggio con parametri specifici. Il messaggio
è un messaggio parametrizzato dal parametro 0. Si tratta di un parametro posizionale. Il metodo getText('double.format', {double2}) assegnerà il numero double2 al parametro 0. Infine, viene richiesto il valore di double2 nel formato numerico localizzato. In Francia, il numero 4,56 verrà localizzato nella stringa "4,56".
Dopo questa trasformazione, si ripetono i test.
![]() |
Fin dalla visualizzazione iniziale del modulo, si riscontra un'anomalia in [1]. Torniamo al tag:
<s:textfield name="double2" key="double2.prompt" value="%{getText('double.format',{double2})}"/>
Alla visualizzazione iniziale, il valore del modello double2 è null, un valore non numerico. Modifichiamo il tag come segue:
<s:textfield name="double2" key="double2.prompt" value="%{double2==null ? '' : getText('double.format',{double2})}"/>
Questa volta si verifica se double2==null. In caso affermativo, viene visualizzata una stringa vuota.
Una volta apportata questa modifica, riprendiamo i test:
![]() |
![]() |
Le schermate da [1] a [4] mostrano che il problema che si cercava di risolvere è stato risolto:
- in [1], si inserisce 4,67
- in [2], questo numero è stato accettato
- in [3], viene visualizzato nuovamente come 4,67 con la virgola, il che è confermato da [4].
Purtroppo, i problemi non finiscono qui. Diamo un'occhiata alla sequenza seguente:
![]() |
- in [5], aggiungendo un carattere per invalidare double2
- in [6], i test di convalida hanno funzionato e l'errore viene segnalato. Tuttavia, la stringa visualizzata in [6] non è quella errata, bensì il valore attuale del modello double2. Il valore inserito è andato perso.
Quando si passa da [5] a [6], la richiesta non viene portata a termine. Viene interrotta dall’intercettatore di convalida. In [6] è stato visualizzato il valore del modello double2, che non ha ricevuto un nuovo valore a causa di tale interruzione. Viene quindi visualizzato il suo valore precedente, mentre avrebbe dovuto essere visualizzata la stringa inserita. I parametri di una richiesta sono visualizzabili tramite la notazione #parameters['param']. Si modifica il campo di immissione double2 nel modo seguente:
<s:textfield name="double2" key="double2.prompt" value="%{#parameters['double2']!=null ? #parameters['double2'] : double2==null ? '' : getText('double.format',{double2})}"/>
Il valore visualizzato del campo double2 viene calcolato come segue: se il parametro 'double2' esiste, viene visualizzato; in caso contrario, viene visualizzato il modello double2. Si invita il lettore a verificare che questa nuova versione del tag risolva i problemi riscontrati.
13.10. Conclusion
È sorprendente che l’inserimento di numeri reali con controllo di validità sia così complicato... Forse mi è sfuggito qualcosa nella documentazione?









