13. Esempio 10 – Conversione e convalida di numeri reali
La nuova applicazione consente l'immissione di numeri reali:
![]() |
- in [1], il modulo di immissione
- in [2], la risposta restituita
L'applicazione funziona in modo simile all'input di numeri interi, quindi 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
- [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 per l'azione [FormDouble]. Questo file delega tali convalide al modello utilizzando il metodo appena descritto.
- [FormDoubleModel]: il modello per l'azione [FormDouble]
- [FormDoubleModel-validation.xml]: le regole di validazione 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>
È simile a quello discusso per l'inserimento di numeri interi.
13.3. 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 svolge un ruolo importante. Ci torneremo più tardi.
13.4. Il modulo di immissione 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 per i numeri reali. I campi da double2 a double5 hanno un attributo di valore complesso. 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, abbiamo dovuto rendere le cose più complesse. Per ora, il lettore può ignorare questa complessità. La spiegheremo più avanti.
13.5. La vista di conferma
La vista di conferma [ConfirmationFormDouble.jsp] è la seguente:

Il 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 pagina [ConfirmationFormDouble.jsp] visualizza semplicemente il [FormDoubleModel]. Le righe 47–49 mostrano come viene visualizzato un numero reale. Vogliamo che questa visualizzazione tenga conto delle impostazioni locali dell'applicazione. A seconda delle impostazioni locali, un numero reale non verrà visualizzato allo stesso modo in Francia (10,7) e nel Regno Unito (10.7). Per ottenere questo risultato, utilizziamo il tag <s:text>, che avevamo già utilizzato in precedenza per l'internazionalizzazione dell'applicazione. Questo tag viene quindi utilizzato 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 è un messaggio, ma un formato di visualizzazione:
- 0 è un parametro che rappresenta il valore da visualizzare. In questo caso, sarà il campo double5 del modello.
- number rappresenta il formato numerico. Sarà 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>
Visualizza il messaggio {0, numero}, dove il parametro double5 (riga 2) sostituisce il parametro 0 nel formato. Pertanto, il valore 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 {
// constructor without parameters
public FormDoubleModel() {
}
// fields
private String double1;
private Double double2;
private Double double3;
private Double double4;
private Double double5;
private String double6;
// raz model
public void clearModel() {
double1 = null;
double2 = null;
double3 = null;
double4 = null;
double5 = null;
double6 = null;
}
// getters and setters
...
}
- I campi double1 e double6 sono di tipo String
- gli altri campi sono di tipo Double
13.7. 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/exemple-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/exemple-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 nelle righe 12–21 verifica che il campo di input double1 corrisponda a un modello di espressione regolare che rappresenta un numero reale.
- La regola nelle righe 23–30 verifica che il campo di input double2 possa essere convertito in un numero reale a doppia precisione.
- La regola nelle righe 32–43 verifica che il campo di immissione `double3` possa essere convertito in un numero reale a doppia precisione maggiore o uguale a 2,64. Si noti che deve essere utilizzata la notazione anglosassone per i numeri reali.
- La regola nelle righe 45–56 verifica che il campo di immissione double4 possa essere convertito in un numero reale a doppia precisione < 8,32.
- La regola nelle righe 58–70 verifica che il campo di input `double5` possa essere convertito in un numero reale a doppia precisione nell'intervallo [2,64, 8,32[.
- La regola nelle righe 72–80 verifica che il campo double6 segua lo schema di un'espressione regolare che rappresenta un numero reale.
Una volta che il file [FormDoubleModel-validation.xml] è stato elaborato dall'intercettatore di validazione, quest'ultimo esegue il metodo validate dell'azione [FormDouble], se esiste. 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 {
// constructor without parameters
public FormDouble() {
}
// action model
public Object getModel() {
if (session.get("model") == null) {
session.put("model", new FormDoubleModel());
}
return session.get("model");
}
@SkipValidation
public String clearModel() {
// close to the model
((FormDoubleModel) getModel()).clearModel();
// result
return INPUT;
}
public String cancel() {
// cleaning the model
((FormDoubleModel) getModel()).clearModel();
// result
return "cancel";
}
// SessionAware
Map<String, Object> session;
public void setSession(Map<String, Object> session) {
this.session = session;
}
// validation
@Override
public void validate() {
// valid double6 entry?
if (getFieldErrors().get("double6") == null) {
// replace the comma with a period in the double6 string
String strDouble6 = (((FormDoubleModel) getModel()).getDouble6()).replace(',', '.');
// String --> double
double double6 = Double.parseDouble(strDouble6);
// check
if (double6 < 2.64 || double6 > 8.32) {
addFieldError("double6", getText("double6.error"));
}
}
}
}
L'azione [FormDouble] è basata sullo stesso modello dell'azione [FormInt]. Ci limiteremo a commentare il metodo validate. Si noti che il metodo validate viene eseguito dopo l'elaborazione del file di validazione [FormDoubleModel-validation.xml] e prima della chiamata al metodo execute.
- Riga 48: se nel campo double6 sono già presenti degli errori, non viene intrapresa alcuna azione.
- riga 50: abbiamo ricevuto una stringa nella forma 45.67. È stata memorizzata nel campo double6 del modello. Sostituiamo la virgola con un punto per ottenere 45.67.
- riga 52: la stringa 45.67 viene convertita in un numero in virgola mobile a doppia precisione. Questo dovrebbe funzionare poiché la stringa nel campo double6 segue il formato di un numero reale.
- Riga 54: Verifichiamo che il double risultante sia compreso nell'intervallo [2,64; 8,32].
- Riga 55: se così non fosse, il messaggio di errore della chiave double6.error viene associato al campo double6. Questo messaggio si trova nel file [FormDoubleModel.properties]. Verrà visualizzato quando il modulo con l'errore verrà visualizzato nuovamente.
13.9. Dettagli finali
Torniamo ora alla complessità dei campi di immissione nel modulo [FormDouble.jsp]. Ci concentreremo sul campo double2. Il ragionamento si applica ai campi da double3 a double5, che hanno un modello Double. Per i campi double1 e double6, che hanno un modello String, non ci sono problemi.
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})}"/>
Cominciamo con il tag più semplice:
<s:textfield name="double2" key="double2.prompt"/>
e vediamo cosa succede:
![]() |
- In [1], convalidiamo il numero double2 corretto. Non è molto chiaro nello screenshot, ma abbiamo inserito il numero con un punto decimale.
- In [2], la schermata di conferma. L'inserimento double2 ha superato i controlli di validazione. Il numero viene visualizzato con il punto decimale.
![]() |
- In [3], torniamo 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], inviamo nuovamente il modulo senza modificare nulla
- In [6], viene segnalato un errore nel campo double2.
Il problema è il seguente:
- Inizialmente in [1], la stringa di input "4,32" è stata convertita con successo nel numero in virgola mobile 4.32. Ciò significa che la conversione da stringa a double ha avuto esito positivo e che, in questo contesto, Struts tiene conto delle impostazioni locali, in questo caso della Francia.
- Il nuovo modulo [4] visualizza il numero reale 4.32 nel campo double2. Poiché la visualizzazione non è stata localizzata, viene utilizzato per impostazione predefinita il formato anglosassone, ovvero con il punto decimale. Quindi, nella direzione da Double a String, Struts non tiene più conto delle impostazioni locali; altrimenti, avrebbe visualizzato 4.32 con la virgola.
Questo è a dir poco incoerente. Ma non importa, localizzeremo la visualizzazione del numero 4,32. Il tag di input 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 di un'espressione OGNL. Si ricordi la definizione della chiave double.format nel file [FormDoubleModel.properties]:
double.format={0,number}
Il metodo getText('key') recupera il messaggio associato a una chiave. Questo messaggio viene cercato nel file corrispondente alle impostazioni locali correnti. Pertanto, se le impostazioni locali fossero es (Spagna), la chiave double.format sarebbe stata cercata nel file [FormDoubleModel_es.properties].
Il metodo getText('key', {param0, param1, ...}) recupera un messaggio parametrizzato. 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. In definitiva, richiediamo il valore di double2 nel formato numerico localizzato. In Francia, il numero 4,56 verrà localizzato come la stringa "4,56".
Dopo questa modifica, eseguiamo nuovamente i test.
![]() |
Non appena il modulo viene visualizzato per la prima volta, si nota un'anomalia in [1]. Torniamo al tag:
<s:textfield name="double2" key="double2.prompt" value="%{getText('double.format',{double2})}"/>
Alla prima visualizzazione, il valore del modello double2 è nullo, ovvero 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, verifichiamo se double2==null. In tal caso, visualizziamo una stringa vuota.
Una volta apportata questa modifica, riprendiamo il test:
![]() |
![]() |
Le schermate da [1] a [4] mostrano che il problema che stavamo cercando di risolvere è stato risolto:
- in [1] inseriamo 4,67
- in [2], questo numero è stato accettato
- in [3], viene visualizzato nuovamente come 4,67 con il punto decimale, il che è confermato da [4].
Purtroppo, i problemi non finiscono qui. Diamo un'occhiata alla seguente sequenza:
![]() |
- In [5], viene aggiunto un carattere che rende non valido double2
- In [6], i controlli di validità hanno funzionato e l'errore viene segnalato. Tuttavia, la stringa visualizzata in [6] non è quella errata, ma il valore corrente del modello double2. Il valore inserito è andato perso.
Passando da [5] a [6], la richiesta non viene completata. Viene interrotta dall'intercettore di validazione. In [6] abbiamo visualizzato il valore del modello double2, che non ha ricevuto un nuovo valore a causa di questa interruzione. Pertanto, viene visualizzato il suo valore precedente, mentre avrebbe dovuto essere visualizzata la stringa inserita. I parametri di una richiesta sono accessibili tramite la notazione #parameters['param']. Aggiorniamo il campo di immissione double2 come segue:
<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; altrimenti, viene visualizzato il template double2. Il lettore è invitato a verificare se questa nuova versione del tag risolve i problemi riscontrati.
13.10. Conclusione
È sorprendente che l'inserimento di numeri reali con controlli di validità sia così complicato... Forse mi è sfuggito qualcosa nella documentazione?








