C'è un modo per sfuggire a un token di fine CDATA in XML?


129

Mi chiedevo se c'è un modo per sfuggire a un token di fine CDATA ( ]]>) all'interno di una sezione CDATA in un documento XML. O, più in generale, se esiste una sequenza di escape da utilizzare all'interno di un CDATA (ma se esiste, immagino che probabilmente avrebbe senso sfuggire ai token di inizio o fine, comunque).

Fondamentalmente, puoi avere un token di inizio o fine incorporato in un CDATA e dire al parser di non interpretarlo ma di trattarlo come un'altra sequenza di caratteri.

Probabilmente, dovresti semplicemente riformattare la tua struttura xml o il tuo codice se ti ritrovi a provare a farlo, ma anche se ho lavorato con xml quotidianamente negli ultimi 3 anni circa e non ho mai avuto questo problema, Mi chiedevo se fosse possibile. Solo per curiosità.

Modificare:

Oltre all'utilizzo della codifica html ...


4
Innanzitutto, accetto la risposta come corretta ma nota: nulla impedisce a qualcuno di codificare >come >all'interno di CData per garantire che Embedded ]]>non venga analizzato come CDEnd. Significa semplicemente che è inaspettato e che &deve essere PRIMA codificato in &modo che i dati possano essere decodificati correttamente. Gli utenti del documento devono sapere anche per decodificare questo CData. Non è inaudito poiché parte dello scopo di CData è di contenere contenuti che un consumatore specifico comprende come gestire. Non si può pretendere che un simile CData venga interpretato correttamente da nessun consumatore generico.
nix,

1
@nix, CDATA fornisce solo un modo esplicito per dichiarare il contenuto del nodo di testo in modo tale che i token di lingua all'interno (diversi da]]>) non vengano analizzati. In particolare non espande i riferimenti alle entità come & gt; per questo motivo, quindi in un blocco CDATA, ciò significa solo quei quattro caratteri, non ">". Per dirla in prospettiva: nelle specifiche xml, tutto il contenuto del testo è chiamato "cdata", non solo queste sequenze ("dati di carattere"). Inoltre non si tratta di specifici agenti di consumo. (Una cosa del genere esiste però: istruzioni di elaborazione (<? Istruzione target?>).
Punto

(Dovrei aggiungere, anche se questo genere di cose va contro l'intento originale del nodo, tutto è giusto nella lunga e tortuosa battaglia con XML. Sento solo che potrebbe essere utile per i lettori sapere che <! [CDATA [ ]]> in realtà non è stato progettato per questo scopo.)
Punto

1
@Semicolon è CDATAstato progettato per consentire qualsiasi cosa : vengono utilizzati per sfuggire a blocchi di testo contenenti caratteri che altrimenti verrebbero riconosciuti come markup Ciò implica CDATAanche poiché è anche markup. Ma, in effetti, non hai bisogno della doppia codifica che ho insinuato. ]]&gt;è un mezzo accettabile per codificare a CDEndall'interno di a CDATA.
nix,

È vero, non avresti bisogno della doppia codifica, ma avresti comunque bisogno che l'agente avesse una conoscenza speciale, dal momento che il parser non avrebbe analizzato & gt; come>. Questo è quello che vuoi dire, penso? Che potresti sostituirli come ritieni opportuno, dopo aver analizzato?
Punto

Risposte:


141

Chiaramente, questa domanda è puramente accademica. Fortunatamente, ha una risposta molto precisa.

Non è possibile evitare una sequenza finale CDATA. La regola di produzione 20 della specifica XML è abbastanza chiara:

[20]    CData      ::=      (Char* - (Char* ']]>' Char*))

EDIT: Questa regola del prodotto significa letteralmente "Una sezione CData può contenere tutto ciò che vuoi MA la sequenza ']]>'. Nessuna eccezione.".

EDIT2: la stessa sezione recita anche:

All'interno di una sezione CDATA, solo la stringa CDEnd viene riconosciuta come markup, in modo che le parentesi angolari sinistre e le e commerciali possano verificarsi nella loro forma letterale; non devono (e non possono) essere evitati usando " &lt;" e " &amp;". Le sezioni CDATA non possono essere nidificate.

In altre parole, non è possibile utilizzare riferimenti a entità, markup o qualsiasi altra forma di sintassi interpretata. L'unico testo analizzato all'interno di una sezione CDATA è]]> e termina la sezione.

Quindi, non è possibile fuggire ]]> all'interno di una sezione CDATA.

EDIT3: la stessa sezione recita anche:

2.7 Sezioni CDATA

[Definizione: le sezioni CDATA possono verificarsi ovunque si verifichino dati sui caratteri; sono usati per sfuggire a blocchi di testo contenenti caratteri che altrimenti verrebbero riconosciuti come markup. Le sezioni CDATA iniziano con la stringa "<! [CDATA [" e terminano con la stringa "]]>":]

Quindi potrebbe esserci una sezione CDATA ovunque possano verificarsi dati sui caratteri, incluse più sezioni CDATA adiacenti al posto di una singola sezione CDATA. Ciò consente di dividere il ]]>token e metterne le due parti in sezioni CDATA adiacenti.

ex:

<![CDATA[Certain tokens like ]]> can be difficult and <invalid>]]> 

dovrebbe essere scritto come

<![CDATA[Certain tokens like ]]]]><![CDATA[> can be difficult and <valid>]]> 

1
Infatti. Beh, non sono un tipo accademico ma, come ho detto nella domanda, sono solo curioso di questo. Ad essere sincero, prenderò semplicemente le tue parole su questo, perché riesco a malapena a capire la sintassi usata per la regola. Grazie per la tua risposta.
Juan Pablo Califano,

39
Questa non è una domanda accademica. Pensa a un feed RSS di un post sul blog che contiene una discussione su CDATA.
usr

4
Intendevo "accademico" nel senso: "interessante da discutere, ma senza uso pratico". Generalmente, CDATA non è utile, è solo un modo per serializzare il testo XML ed è semanticamente equivalente a sfuggire a caratteri speciali usando entità carattere & lt; & gt; e & quot ;. Le entità dei personaggi è la soluzione più semplice, più robusta e più generale, quindi usa quella invece delle sezioni CDATA. Se usi una libreria XML corretta (invece di creare XML senza stringhe) non devi nemmeno pensarci.
dada

5
Sono appena stato morso da questo perché sto cercando di codificare alcuni Javascript compressi in un tag <script> come: <script>/*<![CDATA[*/javascript goes here/*]]>*/</script>e il mio javascript include proprio quella sequenza! Mi piace l'idea di dividere in più sezioni CDATA ...
NickZoic

3
L'ho sperimentato nel mondo reale. Durante la lettura del dump di Wikipedia e la scrittura di un altro file XML ho trovato questo sulla pagina per il National Transportation Safety Board . Conteneva $ 100 milioni (2013) per il budget nella casella informativa. Il codice sorgente XML contenuto [[United States dollar|US$]]&gt;100 million (2013)che è stato tradotto [[United States dollar|US$]]>100 million (2013)dal lettore e dallo scrittore ha optato per l'uso di CDATA per sfuggire al testo e non è riuscito.
Paul Jackson,

169

Devi dividere i tuoi dati in pezzi per nascondere il ]]>.

Ecco tutto:

<![CDATA[]]]]><![CDATA[>]]>

Il primo <![CDATA[]]]]>ha il ]]. Il secondo <![CDATA[>]]>ha il >.


1
Grazie per la tua risposta. Stavo piuttosto cercando qualcosa di simile a una barra rovesciata (all'interno di stringhe in C, PHP, Java, ecc.). Secondo la regola citata da ddaa, sembra che non ci sia nulla del genere.
Juan Pablo Califano,

28
Questa dovrebbe essere la risposta accettata. Evadere è un termine leggermente ambiguo, ma questa risposta affronta sicuramente lo spirito di evasione . Peccato che non si adatti alla concezione ristretta del PO di fuga , che richiede arbitrariamente il carattere barra rovesciata per essere coinvolto per qualche motivo.
G-Wiz,

5
Quindi, in sintesi, scappare ]]>come ]]]]><![CDATA[>. 5 volte la lunghezza ... wow. Ma poi, è una sequenza non comune.
Brilliand

5
Non solo la lunghezza 5x è divertente, non è nemmeno una sequenza non comune nel codice, che è il caso d'uso principale di CDATA! Supponendo JavaScript compresso che rimuove gli spazi, potresti accedere a un campo per nome da una matrice di nomi per indice, come "if (field [fieldnames [0]]> 3)" e ora devi cambiarlo in "if ( field [fieldnames [0]]]]> <! [CDATA [> 3) ", che sconfigge allo scopo di usare CDATA per renderlo più leggibile, LOL. Vorrei schiaffeggiare verbalmente chiunque abbia inventato la sintassi CDATA.
Triynko,

1
Escaping, o più correttamente, la quotazione, significa inserire del testo in un contesto in cui il testo grezzo ha significato SENZA lasciare il contesto. Non ha nulla a che fare con le barre rovesciate. E questa risposta non sta sfuggendo o citando poiché produce due sezioni CDATA anziché una.
ddaa,

17

Non sfuggite al ]]>ma sfuggite al >dopo ]]inserendo ]]><![CDATA[prima del >, pensate a questo proprio come in una \stringa C / Java / PHP / Perl ma serve solo prima di un >e dopo un ]].

BTW,

La risposta di S.Lott è la stessa di questa, formulata diversamente.


2
Preferisco questa formulazione. :)
Brilliand

3
Questo modo di dire dà alla gente un'idea sbagliata. Questo non sta scappando. ]]]]><![CDATA[>non è una sequenza magica per ]]>. ]]]]>ha ]]caratteri come dati e ]]>termina la sezione CDATA corrente. <![CDATA[>avvia una nuova sezione CDATA e la inserisce >. In realtà sono due elementi diversi e verranno trattati in modo diverso quando si lavora con un parser DOM. Dovresti esserne consapevole. Questo modo di fare è simile a quello ]]]><![CDATA[]>, tranne per il fatto che inserisce ]il primo e ]>il secondo CDATA. La differenza rimane.
Aidiakapi,

La differenza è sopravvalutata, poiché il contenuto CDATA viene trattato come un arco letterale di testo con escape. Solo quando si scherza con il DOM è davvero importante, e a quel livello hai comunque a che fare con altri confini invisibili come nodi di testo, commenti ed istruzioni di elaborazione.
Beejor,

7

La risposta di S. Lott è giusta: non codifichi il tag di fine, lo spezzi in più sezioni CDATA.

Come affrontare questo problema nel mondo reale: usando un editor XML per creare un documento XML che verrà inserito in un sistema di gestione dei contenuti, prova a scrivere un articolo sulle sezioni CDATA. Il tuo trucco ordinario di incorporare esempi di codice in una sezione CDATA non riuscirà qui. Puoi immaginare come ho imparato questo.

Ma nella maggior parte dei casi, non ti imbatterai in questo, ed ecco perché: se vuoi archiviare (diciamo) il testo di un documento XML come contenuto di un elemento XML, probabilmente utilizzerai un metodo DOM, ad esempio:

XmlElement elm = doc.CreateElement("foo");
elm.InnerText = "<[CDATA[[Is this a problem?]]>";

E il DOM sfugge abbastanza ragionevolmente al <e al>, il che significa che non hai inavvertitamente incorporato una sezione CDATA nel tuo documento.

Oh, e questo è interessante:

XmlDocument doc = new XmlDocument();

XmlElement elm = doc.CreateElement("doc");
doc.AppendChild(elm);

string data = "<![[CDATA[This is an embedded CDATA section]]>";
XmlCDataSection cdata = doc.CreateCDataSection(data);
elm.AppendChild(cdata);

Questa è probabilmente un'ideosincrasia del DOM .NET, ma ciò non fa eccezione. L'eccezione viene generata qui:

Console.Write(doc.OuterXml);

Immagino che ciò che sta accadendo sotto il cofano sia che XmlDocument sta usando un XmlWriter per produrre il suo output, e XmlWriter controlla la sua buona formazione mentre scrive.


Bene, ho avuto un esempio quasi "reale". Di solito carico Xml da Flash che contiene markup HTML nelle sezioni CDATA. Avere un modo per scappare potrebbe essere utile, immagino. Ma comunque, in quel caso, il contenuto CDATA è di solito XHTML valido, e quindi il CDATA "esterno" potrebbe essere evitato del tutto.
Juan Pablo Califano,

2
CDATA può quasi sempre essere evitato del tutto. Trovo che le persone che lottano con CDATA molto frequentemente non capiscano cosa stanno davvero cercando di fare e / o come la tecnologia che stanno usando funziona davvero.
Robert Rossney,

Oh, dovrei anche aggiungere che l'unica ragione per cui il CMS a cui ho accennato nella mia risposta ha usato CDATA era che l'ho scritto e non capivo cosa stavo davvero cercando di fare e / o come funziona la tecnologia. Non avevo bisogno di usare CDATA.
Robert Rossney,

Se stai usando .net, il precedente commento sull'evitabilità di CDATA è perfetto: basta scrivere il contenuto come una stringa e il framework farà tutto il escape (e l'escaping in lettura) per te dal mondo reale .... ... xmlStream.WriteStartElement ("UnprocessedHtml"); xmlStream.WriteString (UnprocessedHtml); xmlStream.WriteEndElement ();
Mark Mullin,


3

Ecco un altro caso in cui ]]>deve essere evitato. Supponiamo di dover salvare un documento HTML perfettamente valido all'interno di un blocco CDATA di un documento XML e che l'origine HTML abbia il proprio blocco CDATA. Per esempio:

<htmlSource><![CDATA[ 
    ... html ...
    <script type="text/javascript">
        /* <![CDATA[ */
        -- some working javascript --
        /* ]]> */
    </script>
    ... html ...
]]></htmlSource>

il suffisso CDATA commentato deve essere modificato in:

        /* ]]]]><![CDATA[> *//

poiché un parser XML non saprà come gestire i blocchi di commenti javascript


Questo non è un caso speciale. Sostituisci semplicemente ]]>con ]]]]><![CDATA[>si applica ancora qui. Il fatto che sia JavaScript o commentato non è importante.
Thomas Grainger,

1

In PHP: '<![CDATA['.implode(explode(']]>', $string), ']]]]><![CDATA[>').']]>'


1

Un modo più pulito in PHP:

   function safeCData($string)
   {
      return '<![CDATA[' . str_replace(']]>', ']]]]><![CDATA[>', $string) . ']]>';
   }

Non dimenticare di utilizzare uno str_replace sicuro multibyte, se necessario (non latino1 $string):

   function mb_str_replace($search, $replace, $subject, &$count = 0)
   {
      if (!is_array($subject))
      {
         $searches = is_array($search) ? array_values($search) : array ($search);
         $replacements = is_array($replace) ? array_values($replace) : array ($replace);
         $replacements = array_pad($replacements, count($searches), '');
         foreach ($searches as $key => $search)
         {
            $parts = mb_split(preg_quote($search), $subject);
            $count += count($parts) - 1;
            $subject = implode($replacements[$key], $parts);
         }
      }
      else
      {
         foreach ($subject as $key => $value)
         {
            $subject[$key] = mb_str_replace($search, $replace, $value, $count);
         }
      }
      return $subject;
   }

Puoi spiegare il tuo downvote? Dire che ho fatto un errore non è utile quanto spiegare dove si trova.
Alain Tiemblo,

Non è necessario eseguire la sostituzione sicura multibyte se si utilizza UTF-8. Tuttavia, non ho
votato in negativo

-1

Non penso che interrompere CDATA sia una buona strada da percorrere. Ecco la mia alternativa ...

Usa ]per la sequenza di escape seguita dal valore esadecimale del tuo personaggio. Come in &#xhhhh;=>]<unicode value>;

In questo modo se provi a registrare la ]]>tua codifica fn produrrà il ]005D;]005D;]003E;che è ok in CDATA.

È meglio che scappare per nome dell'entità, perché quelli non vengono decodificati ogni volta nella tua app e potresti avere priorità diverse per sfuggire alle entità con e commerciale rispetto a scappare alcuni altri caratteri / sequenze. Di conseguenza hai un maggiore controllo sul contenuto di CDATA.


-2

Vedi questa struttura:

<![CDATA[
   <![CDATA[
      <div>Hello World</div>
   ]]]]><![CDATA[>
]]>

Per i tag CDATA interni è necessario chiudere ]]]]><![CDATA[>invece di ]]>. Semplice come quella.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.