In questo articolo Java esporrò una soluzione custom per risolvere il problema del trasporto e salvataggio di dati con codifica UTF-8, provenienti dal WEB, attraverso diversi strati software e differenti codifiche.
Contesto
Internazionalizzazione di un applicativo web based di supporto ad alcune banche estere per l'inserimento di prospects della clientela. Gli istituti appartengono tutti all'area dell'Est Europeo: Česká Republika, Hungary e Republika Hrvatska.
L'applicazione rientra nella categoria delle JEE Applications e si compone di diversi strati software, i quali utilizzano ognuno una codifica particolare e ben definita.
Lo strato web è costituito da HTML e JavaScript per la parte client e dall'accoppiata JSP / Struts lato server.
L'application server fornisce connettività a diverse risorse tra le quali il CICS aziendale e Datasources a MS SQL Server, ORACLE 11g ed IBM DB2.
Purtroppo ogni suddetto strato software è stato configurato con un charset differente e, ad esclusione di SQL Server ed ORACLE che sono di recente installazione, manco per farlo apposta tra i più restrittivi:
CICS è configurato per ricevere ed inviare i dati in formato EBCDIC mentre il database DB2 utilizza la codifica Latin1.
In questa situazione non basta, ovviamente, settare la HTTP Request per l'utilizzo della codifica UTF-8: questa garantisce solo il corretto riconoscimento dei caratteri che non fanno parte del set installato sul server (es: Č), ma basta per dire che lo strato WEB garantisce il trasporto di dati codificati con il charset UTF-8.
Per quanto riguarda invece il corretto trasporto e salvataggio dei dati attraverso il CICS aziendale ho provato ad utilizzare delle funzionalità messe a disposizione dalle API del SDK1.3, piattaforma su cui si fonda l'applicativo.
Tra le informazioni trovate a riguardo vi erano un paio di metodi molto simili che pensavo facessero al caso mio.
Di questi ne mostro uno solo perchè logicamente sono molto simili, ed uguali nel risultato, ovvero non funzionavano come mi sarei aspettato:
/* convert from internal Java String Format -> UTF-8
* encoded HTML/JSP-Pages
*/
public static String convertToUTF8(String s) {
String out = null;
try {
out = new String(s.getBytes("UTF-8"));
} catch (java.io.UnsupportedEncodingException e) {
return null;
}
return out;
}
Il secondo metodo trasforma il charset encoding di un carattere alla volta e poi li concatena ottenendo la nuova stringa codificata in UTF-8, la quale risulta essere differente dal quella restituita dal metodo convertToUTF8(String s) sopra mostrato. Ma entrambe non restituiscono ciò che mi aspettavo.
Perchè i metodi standard di Java sbagliano?
La cosa strana è che alcuni caratteri venivano codificati correttamente ed altri no.
Dopo 1 giorno di analisi veramente a basso livello, ho capito che l'errore si verificava al momento di trasformare il carattere che in decimale (base 10) aveva un valore superiore a 256 e questo errore influenzava poi tutte le trasformazioni dei caratteri successivi. Si provi a codificare con il suddetto metodo la stringa "¢¡¶©þýåÜýáýáííéíá¢" e successivamente rileggerla in una pagina HTML.
La mia soluzione
Dunque, scoperto il problema ci si attrezza per superare l'ostacolo!
La soluzione sta nel parsare i dati per trasformare in entità HTML ogni carattere la cui rappresentazione numerica non rientra in un range predeterminato.
Una entità è la rappresentazione di un carattere o un simbolo che viene trattato in maniera particolare dal browser e dai parser XML ; ogni entità inizia con il carattere "&" e termina con il ";".
Sono famose le entità per esprimere quei caratteri vietati in XML come ">" e "<" che devono essere scritti come "& gt;" e "& lt;".
Ma per poter esprimere caratteri e simboli avendone la rappresentazione decimale le entità prendono la seguente forma "&#nnn;".
La soluzione è rappresentata da un metodo, scritto da me, che ricavato il valore numerico di ogni carattere lo concatena in una stringa per creare la corrispondente entità.
Questo metodo, che ora esporrò, funziona correttamente e mi permette di non dover elaborare le stringhe salvate in DB2 prima di mostrarle perchè sono già XML well-formed e correttamente interpretate dai browsers.
/**
* Trasforma tutti i caratteri UTF-8
* che non esistono nel set di caratteri ISO-8859-1
* nella relativa entità (&#nnn;)
*
* @param utf8 stringa UTF-8 da codificare in ISO-8859
* @return la stringa codificata.
*/
public static String encodeUTF8ToASCII(String utf8){
if(utf8.equals(null) || utf8.length == 0)
return "";
StringBuffer sb = new StringBuffer();
char[] c = utf8.toCharArray();
for(int i=0; i<c.length; i++){
if(32 <= (int)c[i] && (int)c[i]<= 122)
sb.append(c[i]);
else
sb.append("&#" + (int)c[i] + ";");
}
return sb.toString();
}
Grazie a questo semplice metodo statico è possibile salvare e rileggere correttamente dati attraverso differenti strati software senza alcuna perdita di dati.
Alla prossima,
MA
0 commenti:
Posta un commento
Non ti è chiaro qualcosa?
No problem, posta il tuo dubbio ;)
..... e ricordati di firmarlo!