Vita da tecnici

Entries categorized as ‘xml caratteri accentati’

Java encoding addedum no. 5: il peccato capitale String.getBytes()

Gennaio 31, 2008 · 1 Commento

Si conclude con questo articolo la panoramica sull’encoding in Java e sulle ragioni delle eccezioni che spesso si incontrano nel fare il parsing dei file xml.

Questo ultimo articolo è dedicato alla fonte più ricorrente di tali errori. la chiamata del metodo getBytes della classe String senza parametri (parametri nella getBytes?? eh sì, esite anche la getBytes che accetta un argomento). L’esperienza mi insegna che dove c’è una getBytes() e del parsing xml prima o poi compaiono errodi di encoding. Perchè?

Perchè effettua l’encoding usando il charset della piattaforma su cui gira java e quindi varia da sistema operativo a sistema operativo. Chi riceve la sequenza di caratteri tradotta in byte non sa che encoding è stato usato, per cui si può solo indovinare. Se proprio serve serializzare dei caratteri in uno stream di byte la prima cosa da fare è concordare l’encoding in modo che chi riceve e chi trasmette possa scambiarsi i dati. Consiglio di utilizzare UTF-8 che consente di inviare caratteri di vario tipo (anche quelli non europei). Neanche a farlo apposta in questi giorni mi sono scontrato con del mio codice (sigh) che per fare il parsing di un file xml richiede come parametro un InputStream; peccato che la funzione chiamante passasse una stringa. Per passare da una stringa ad un inputstream si fa getBytes e poi si passa il risultato ad un ByteArrayInputStream. E zac!! ci si espone ad errori di encoding perchè i parser leggono da InputStream l’encoding da utilizzare che non c’entra nulla con quello dello stream che è ottenuto da una getBytes. La soluzione è di aggiungere un metodo che accetta come parametro un reader e usare un StringReader.

Categorie: Pensieri sparsi · java encoding · xml caratteri accentati · xml encoding

Java encoding addedum no. 4 xml all’interno dell’xml

Gennaio 22, 2008 · Nessun Commento

Questo è il penultimo dei post che riguarda l’xml encoding e tratta un argomento che si può presentare quando si ha a che fare con delle applicazioni che si basano su scambio di messaggi xml. Capita a volte che le applicazioni message oriented abbiano una struttura a due livelli:

  1. il primo livello di xml trasmette i campi generali (es il mittente, il nome del servizio, il contenuto informativo)
  2. il secondo livello di xml trasmette il contenuto vero proprio ed è rappresentato da un campo del primo livello

Esempio

<message>

<sender>Giovanni</sender>

<service>invio_fattura</service>

<content><![CDATA[

<?xml version="1.0" encoding="ISO-8859-1"?>
<fattura> 	<numero>2</numero>  	<data>20080131</data> ... ...  </fattura>

]]></content>

</message>

Tale strutturazione si usa quando si vuole creare uno scheletro di comunicazione da usare per diversi servizi che vengono chiesti a dei fornitori in tempi diversi. Gli sviluppi successivi possono riutilizzare “l’infrastruttura” di comunicazione e concentrarsi solo sul contenuto senza dover configurare endpoint diversi e “menate varie”.

Una soluzione di questo tipo pone un problema di encoding particolare; infatti per evitare il famoso Invalid byte 2 of 3-byte UTF-8 sequenceed annessi e connessi è necessario capire cosa succede se il messaggio viene trasportato via rete tramite protocollo http.

Il contenuto del tag content è a tutti gli effetti una stringa che, nel caso di cdata non viene toccata (si può inserire il contenuto facendo l’escaping dei caratteri, ma il messaggio diventa molto meno leggibile ad occhio nudo). La domanda da porsi è: se all’interno di CDATA ci sono dei caratteri accentati con che encoding vengono trasmessi?

La risposta è: a priori non è dato saperlo. Eh? Perchè?

Analizziamo il “viaggio” del messaggio; se si usa un web service client (tipo quelli generati da AXIS) abbiamo a disposizione un metodo di un oggetto che, presi i parametri di input, crea un xml e tramite un http client, lo invia “come stringa” (è una semplificazione, ma serve per non perdersi nei dettagli) all’http server. l’http client specifica un encoding nell’header http; il server legge l’encoding e ricostruisce una stringa che rappresenta il messaggio xml. A questo punto sul server il web service può essere implementato tramite un metodo che accetta gli stessi parametri del client (stile AXIS) o viene fornito un albero JDOM, DOM4J o XOM da cui prelevare i dati (stile Spring Web Services). In entrambi i casi il contenuto del campo content è accedibile come stringa. Stringa = sequenza di caratteri per cui non esiste problema di encoding, la conversione byte-caratteri è stata presa in carico dall’http server o dalla libreria che implementa i meccanismi di web service.

Che problemi ci sono allora?

Che il contenuto di content deve essere dato in pasto alla nostra libreria XML preferita che crea un albero o una serie di eventi; ma nell’header del messaggio contenuto in content è specificato un encoding che non ha più alcun legame con l’encoding reale. Tale informazione sull’encoding va ignorata.

Come si fa?

La soluzione l’ho citata nel post precedente: si usa un reader al posto di un input stream.

Ricetta facile e veloce: se dobbiamo dare in pasto ad una libreria xml del contenuto che proviene da una stringa utilizziamo un reader e non un inputstream

Categorie: change encoding · java encoding · xml caratteri accentati · xml encoding

Java encoding addedum no. 3: InputStream vs Reader

Dicembre 4, 2007 · Nessun Commento

In questo articolo porrò le fondamenta di quello che sarà l’argomento di un altro post, l’xml all’interno dell’xml.

Capire la differenza fra Reader ed InputStream è fondamentale se si vuole riuscire ad evitare errori di vario tipo legati all’encoding.

Quel e’ la differenza fra InputStream e Reader?

Il primo ragiona in byte, il secondo in caratteri che tradotto significa: il primo non fa il decoding il secondo sì. (per OutputStream e Writer si applica il concetto complementare di encoding)

La differenza è “tutta” qui. Vediamo cosa significa: se in un programma che tratta XML (dove quindi l’encoding/decoding è richiesto) passo un InputStream cosa fa la libreria(DOM4J per esempio)? Legge la prima riga, il prologo con <?xml cerca l’encoding e costruisce un Reader che trasforma i byte in caratteri secondo le regole del charset. Se invece alla libreria passo un reader, essa comincia  caricare i caratteri senza controllare l’encoding del prologo.

Quindi in un caso il prologo viene letto (e quindi viene fatta una operazione di decoding) nell’altro viene ignorato (il decoding lo ha già fatto il reader).

Sapere che esistono queste due possibilità è molto utile infatti ci sono casi dove il prologo e la relativa informazione sull’encoding deve essere letta (per esempio quando si legge da file system), mentre ce ne sono altri in cui deve essere ignorata (per esempio quando l’xml viene passato come stringa).

Nel prossimo post affronterò l’ulteriore complicazione dell’xml dentro xml, caso che può essere abbastanza frequente nel caso di webservice che si scambiano nessaggi.

Categorie: change encoding · java encoding · xml caratteri accentati · xml encoding

Java XML encoding addendum n.2: i caratteri accentati

Novembre 14, 2007 · 2 Commenti

Dalle statitiche del blog vedo che uno dei termini più richiesti è xml caratteri accentati. Questo posto vuole provare a dare una soluzione di come si trattano i caratteri accentati.

Supponiamo di avere una tag il cui contenuto è:

àèìòù€

e di voler creare un file xml che contenga tali caratteri.

L’esempio con DOM4J è il seguente :

import java.io.FileOutputStream;

import org.dom4j.Document;
import org.dom4j.DocumentFactory;
import org.dom4j.Element;
import org.dom4j.io.OutputFormat;
import org.dom4j.io.XMLWriter;

public class TestXMLEncodingAccento {

public static void main(String[] args) throws Exception {
Document document=DocumentFactory.getInstance().createDocument();
Element e=document.addElement(”CaratteriAccentati”);
e.setText(”àèìòù€”);
OutputFormat outformat = OutputFormat.createPrettyPrint();
outformat.setEncoding(”ISO-8859-1″);
//outformat.setEncoding(”UTF-8″);
//outformat.setEncoding(”ISO-8859-15″);
FileOutputStream fos=new FileOutputStream(”c:/temp/testaccenti.xml”);
XMLWriter writer = new XMLWriter(fos, outformat);
writer.write(document);
writer.flush();
fos.close();
}

}

Il programma non fa altro che creare una stringa con i caratteri in questione, metterli in un tag e salvare un file con un certo encoding. L’encoding è la chiave di tutto in quanto determina il valore dei byte che rappresentato il carattere che vengono poi salvati su disco. “L’equazione” è carattere->sequenza di byte dipendente dall’encoding->file.

Se proviamo ognuno dei tre tipi di encoding e ogni volta apriamo il file con un editor “evoluto” dovremmo vedere i caratteri accentati sempre visualizzati correttamente. Se proviamo ad utilizzare come output System.out il risultato dipende dall’encoding della console in uso, se coincide vediamo tutto corretto altrimenti compaiono dei caratteri strani. Se compaiono dei caratteri strani (tranne per il simbolo dell’euro che è un discorso a parte) significa che il lettore non sa gestire l’encoding, ma il file è scritto bene.

Un discorso a parte merita il simbolo dell’euro, questo compare solo nel caso di encoding UTF-8 e ISO-8859-15. Perchè? Perchè ISO-8859-1 non ha nella sua mappa di traduzione simbolo->sequenza di byte tale carattere per cui non è possibile salvare il simbolo € correttamente. ISO-8859-15 è stato fatto apposta per supportare il simbolo dell’euro (e visto che la codifica utilizza un solo byte per fare spazio all’euro è stato rimosso un altro simbolo).

Alla prossima

Categorie: Pensieri sparsi · java encoding · xml caratteri accentati · xml encoding