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:
- il primo livello di xml trasmette i campi generali (es il mittente, il nome del servizio, il contenuto informativo)
- 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 sequence” ed 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