Home > Codice > Sezioni CDATA con XmlBeans

Sezioni CDATA con XmlBeans

Le sezioni CDATA rappresentano, per le librerie di serializzazione Java <-> Xml, una questione fastidiosa. Non sono marcatori veri e propri, e nel recuperare il contenuto testuale di un nodo vengono ignorate. Il problema sorge nel momento in cui una libreria deve convertire un oggetto in una stringa Xml; per ogni nodo da creare, le opzioni possibili sono quattro:

  1. ignorare i dettagli del contenuto del nodo, e serializzarlo così come è stato valorizzato – questo espone al rischio di produrre in output xml malformato;
  2. racchiudere sempre il contenuto di un nodo in una sezione CDATA; anche questo non è un comportamento ottimale, dal momento che 1) non sempre tale sezione è realmente necessaria, e inoltre 2) aumenta le dimensioni dell’output. In certi contesti d’uso questo può rappresentare un problema significativo;
  3. analizzare il contenuto del nodo e sostituire, ove necessario, determinati caratteri con le corrispondenti entities (ad es. <) o character references (ad es. &#nnn;, dove nnn è l’identificativo Unicode del carattere);
  4. analizzare il contenuto del nodo e, se presenti caratteri che richiederebbero una sostituzione, racchiudere il tutto in una sezione CDATA.

XmlBeans consente di gestire esplicitamente il contenuto dei nodi durante la serializzazione solo a partire dalla versione 2.3.0, tramite due opzioni configurabili con la classe XmlOptions (con i metodi setSaveCDataLengthThreshold e setSaveCDataEntityCountThreshold).

L’ultima release disponibile del ramo di sviluppo precedente, ovvero la 1.0.4, decide invece unilateralmente quando racchiudere il contenuto di un nodo in una sezione CDATA e quando, invece, limitarsi a sostituire i caratteri che contiene. Di fatto, si tratta di una combinazione delle opzioni 3) e 4) sopra esposte.

L’algoritmo utilizzato è, dunque, abbastanza arbitrario (per i curiosi, è implementato nel metodo privato entitizeContent, nella inner class Parse, all’interno della classe org.apache.xmlbeans.impl.store.Saver). In due parole, questa versione di XmlBeans inserisce delle sezioni CDATA solo se:

  • il testo contenuto nel nodo supera i 32 caratteri;
  • tale testo contiene almeno 6 caratteri che andrebbero trasformati in entities;
  • i caratteri da trasformare costituiscono più dell’un per cento del testo totale.

In tutti gli altri casi, via di entitizeContent.

Essendomi trovato a dover utilizzare proprio questa versione della libreria (condivisa da diversi gruppi di lavoro), ho cercato un escamotage di qualche tipo.

Il risultato è il seguente:

public static void main(String[] args) throws Exception {
	/* Our test XML */
	String xml = "<root><first></first><second></second></root>";

	/* Let's build our XmlObject based
	 * on the XML string just defined */
	XmlObject xmlObject = XmlObject.Factory.parse(xml);

	/* The problematic content */
	String content = "Ampersand and less-than: & <";

	/* We'll add this content through a cursor */
	XmlCursor cursor = xmlObject.newCursor();
	cursor.toNextToken();
	cursor.toChild("first");

	/* The first node shall have our "raw" content */
	cursor.setTextValue(content);

	/* The second node shall have our content
	 * in a CDATA section */
	cursor.toNextSibling();
	cursor.setTextValue(XmlHelper.insertCDATATrigger(content));

	/* Here's the result */
	System.out.println(XmlHelper.getStringFromXmlBean(xmlObject));

	/*
	 * <root>
	 *   <first>Ampersand and less-than: & <</first>
	 *   <second><![CDATA[Ampersand and less-than: & <]]></second>
	 * </root>
	 */
}

Ogni volta che devo impostare il contenuto di un nodo, e voglio che venga racchiuso in una sezione CDATA, utilizzo il metodo insertCDATATrigger. Per serializzare il bean, utilizzo getStringFromXmlBean (a me serviva in output una stringa, ma il sistema è valido anche se usate uno degli altri metodi save).

Il file completo lo trovate qui: XmlHelper.java (556) - 4.45 KB

L’eleganza della soluzione è opinabile… ma, a meno di voler re-implementare la serializzazione, o modificare il codice degli XmlBean utilizzati (che, per inciso, non esiste, dal momento che preferisco generare direttamente le classi a partire da file .xsd), mi è sembrata quella più sensata.

  1. Nessun commento ancora...
  1. Nessun trackback ancora...