Existe-t-il un moyen d'échapper à un jeton de fin CDATA en XML?

129

Je me demandais s'il existe un moyen d'échapper à un jeton de fin CDATA ( ]]>) dans une section CDATA dans un document xml. Ou, plus généralement, s'il existe une séquence d'échappement à utiliser dans un CDATA (mais si elle existe, je suppose que cela n'aurait probablement de sens que d'échapper aux jetons de début ou de fin, de toute façon).

Fondamentalement, pouvez-vous avoir un jeton de début ou de fin incorporé dans un CDATA et dire à l'analyseur de ne pas l'interpréter mais de le traiter comme une autre séquence de caractères.

Probablement, vous devriez simplement refactoriser votre structure xml ou votre code si vous essayez de le faire, mais même si je travaille quotidiennement avec xml depuis environ 3 ans et que je n'ai jamais eu ce problème, Je me demandais si c'était possible. Juste par curiosité.

Éditer:

Autre que l'utilisation du codage html ...

Juan Pablo Califano
la source
4
Tout d'abord, j'accepte la réponse comme correcte, mais notez: rien n'empêche quelqu'un d'encoder >comme >dans CData pour garantir que intégré ]]>ne sera pas analysé comme CDEnd. Cela signifie simplement que c'est inattendu et que cela &doit d'abord être encodé &pour que les données puissent être correctement décodées. Les utilisateurs du document doivent également savoir décoder ce CData. Ce n'est pas rare car une partie de l'objectif de CData est de contenir du contenu qu'un consommateur spécifique comprend comment gérer. On ne peut pas s'attendre à ce qu'un tel CData soit interprété correctement par un consommateur générique.
nix
1
@nix, CDATA fournit simplement un moyen explicite de déclarer le contenu du nœud de texte de telle sorte que les jetons de langue dans (autre que]]>) ne soient pas analysés. Il ne développe pas spécifiquement les références d'entités telles que & gt; pour cette raison, donc dans un bloc CDATA, cela signifie simplement ces quatre caractères, pas '>'. Pour mettre les choses en perspective: dans la spécification xml, tout le contenu du texte est appelé "cdata", pas seulement ces séquences ("données de caractère"). Il ne s'agit pas non plus d'agents consommateurs spécifiques. (Une telle chose existe cependant - instructions de traitement (<? Instruction cible?>).
Point
(Je devrais ajouter que même si ce genre de chose va à l'encontre de l'intention originale du nœud, tout est juste dans la longue et tortueuse bataille contre XML. Je pense juste qu'il pourrait être utile pour les lecteurs de savoir que <! [CDATA [ ]]> n'a pas été réellement conçu à cette fin.)
Point
1
@Semicolon a CDATAété conçu pour autoriser tout : ils sont utilisés pour échapper des blocs de texte contenant des caractères qui seraient autrement reconnus comme du balisage Cela implique CDATAaussi puisqu'il s'agit également de balisage. Mais, en fait, vous n'avez pas besoin du double encodage que j'impliquais. ]]&gt;est un moyen acceptable de coder un CDEnddans un CDATA.
nix
Certes, vous n’avez pas besoin de double codage - mais vous auriez toujours besoin de l’agent pour avoir des connaissances spéciales, puisque l’analyseur n’analyserait pas & gt; comme>. C'est ce que tu veux dire, je pense? Que vous pourriez les remplacer comme bon vous semble, après analyse?
Point

Réponses:

141

Clairement, cette question est purement académique. Heureusement, il a une réponse très précise.

Vous ne pouvez pas échapper à une séquence de fin CDATA. La règle de production 20 de la spécification XML est assez claire:

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

EDIT: Cette règle de produit signifie littéralement "Une section CData peut contenir tout ce que vous voulez MAIS la séquence ']]>'. Aucune exception.".

EDIT2: La même section lit également:

Dans une section CDATA, seule la chaîne CDEnd est reconnue comme balisage, de sorte que les crochets et esperluettes gauches peuvent apparaître sous leur forme littérale; ils n'ont pas besoin (et ne peuvent pas) être échappés en utilisant " &lt;" et " &amp;". Les sections CDATA ne peuvent pas être imbriquées.

En d'autres termes, il n'est pas possible d'utiliser une référence d'entité, un balisage ou toute autre forme de syntaxe interprétée. Le seul texte analysé à l'intérieur d'une section CDATA est ]]>, et il termine la section.

Par conséquent, il n'est pas possible de s'échapper ]]>dans une section CDATA.

EDIT3: La même section lit également:

2.7 Sections CDATA

[Définition: les sections CDATA peuvent apparaître partout où des données de caractères peuvent apparaître; ils sont utilisés pour échapper des blocs de texte contenant des caractères qui seraient autrement reconnus comme du balisage. Les sections CDATA commencent par la chaîne "<! [CDATA [" et se terminent par la chaîne "]]>":]

Ensuite, il peut y avoir une section CDATA partout où des données de caractères peuvent apparaître, y compris plusieurs sections CDATA adjacentes à la place d'une seule section CDATA. Cela permet de diviser le ]]>jeton et de placer les deux parties de celui-ci dans des sections CDATA adjacentes.

ex:

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

devrait être écrit comme

<![CDATA[Certain tokens like ]]]]><![CDATA[> can be difficult and <valid>]]> 
ddaa
la source
1
En effet. Eh bien, je ne suis pas du genre universitaire, mais comme je l'ai dit dans la question, je suis simplement curieux de savoir. Pour être honnête, je vais juste vous croire sur parole, car je peux à peine comprendre la syntaxe utilisée pour la règle. Merci pour votre réponse.
Juan Pablo Califano
39
Ce n'est pas une question académique. Pensez à un flux RSS d'un article de blog qui contient une discussion sur CDATA.
usr
4
Je voulais dire "académique" dans le sens: "intéressant à discuter, mais sans utilisation pratique". En général, CDATA n'est pas utile, c'est juste un moyen de sérialiser du texte XML, et il est sémantiquement équivalent à échapper des caractères spéciaux à l'aide d'entités de caractères & lt; & gt; et & quot ;. Les entités de caractères sont la solution la plus simple, la plus robuste et la plus générale, utilisez donc cela à la place des sections CDATA. Si vous utilisez une bibliothèque XML appropriée (au lieu de construire du XML à partir de chaînes), vous n'avez même pas à y penser.
ddaa le
5
Je viens de me faire mordre par celui-ci parce que j'essaye d'encoder du Javascript compressé dans une balise <script> comme: <script>/*<![CDATA[*/javascript goes here/*]]>*/</script>et mon javascript inclut juste cette séquence! J'aime l'idée de diviser en plusieurs sections CDATA ...
NickZoic
3
J'ai vécu cela dans le monde réel. En lisant la décharge de wikipedia et en écrivant un autre fichier xml, j'ai rencontré ceci sur la page du National Transportation Safety Board . Il contenait > 100 millions de dollars américains (2013) pour le budget dans l'infobox. Le xml source contenu [[United States dollar|US$]]&gt;100 million (2013)qui a été traduit [[United States dollar|US$]]>100 million (2013)par le lecteur et l'écrivain a choisi d'utiliser CDATA pour échapper au texte et a échoué.
Paul Jackson
169

Vous devez diviser vos données en morceaux pour masquer le fichier ]]>.

Voici le tout:

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

Le premier <![CDATA[]]]]>a le ]]. Le second <![CDATA[>]]>a le >.

S.Lott
la source
1
Merci pour votre réponse. Je cherchais plutôt quelque chose comme un équivalent de barre oblique inverse (dans des chaînes en C, PHP, Java, etc.). Selon la règle citée par ddaa, il semble qu'il n'y ait pas une telle chose.
Juan Pablo Califano
28
Cela devrait être la réponse acceptée. S'échapper est un terme légèrement ambigu, mais cette réponse aborde définitivement l'esprit d' évasion . Dommage que cela ne corresponde pas à la conception étroite de l'OP de l' échappement , qui oblige arbitrairement la barre oblique inverse à être impliquée pour une raison quelconque.
G-Wiz le
5
Donc en résumé, évadez-vous ]]>comme ]]]]><![CDATA[>. 5 fois la longueur ... wow. Mais alors, c'est une séquence peu commune.
Brilliand
5
Non seulement la longueur 5x est hilarante, mais ce n'est même pas une séquence inhabituelle dans le code, ce qui est le principal cas d'utilisation de CDATA! En supposant que le JavaScript compressé supprime les espaces, vous pourriez accéder à un champ par nom à partir d'un tableau de noms par index, tel que "if (fields [fieldnames [0]]> 3)" et maintenant vous devez le changer en "if ( fields [fieldnames [0]]]]> <! [CDATA [> 3) ", qui va à l'encontre de l'objectif d'utiliser CDATA pour le rendre plus lisible, LOL. Je voudrais gifler verbalement celui qui a proposé la syntaxe CDATA.
Triynko
1
Echapper, ou plus correctement, citer, signifie insérer du texte dans un contexte où le texte brut a un sens SANS quitter le contexte. Cela n'a rien à voir avec les contre-obliques. Et cette réponse n'échappe pas ou ne cite pas car elle produit deux sections CDATA au lieu d'une.
ddaa
17

Vous n'échappez pas au ]]>mais vous échappez à l' >après ]]en insérant ]]><![CDATA[avant le >, pensez à ceci comme une \chaîne en C / Java / PHP / Perl mais nécessaire uniquement avant >et après a ]].

BTW,

La réponse de S.Lott est la même que celle-ci, juste formulée différemment.

Jason Pyeron
la source
2
Je préfère ce libellé. :)
Brilliand
3
Cette façon de dire donne aux gens une mauvaise idée. Ce n'est pas une fuite. ]]]]><![CDATA[>n'est pas une séquence magique pour ]]>. ]]]]>contient des ]]caractères comme données et ]]>termine la section CDATA actuelle. <![CDATA[>démarre une nouvelle section CDATA et y met >. Ce sont en fait deux éléments différents et seront traités différemment lorsque vous travaillez avec un analyseur DOM. Vous devez en être conscient. Cette façon de faire est similaire à ]]]><![CDATA[]>, sauf qu'elle met ]dans le premier et ]>dans le deuxième CDATA. La différence demeure.
Aidiakapi
La différence est surestimée, car le contenu CDATA est traité comme une étendue littérale de texte échappé. Ce n'est que lorsque jouer avec le DOM que cela compte vraiment, et à ce niveau, vous avez de toute façon affaire à d'autres limites invisibles comme le texte, les commentaires et les nœuds d'instructions de traitement.
Beejor
7

La réponse de S. Lott est juste: vous n'encodez pas la balise de fin, vous la divisez en plusieurs sections CDATA.

Comment résoudre ce problème dans le monde réel: en utilisant un éditeur XML pour créer un document XML qui sera introduit dans un système de gestion de contenu, essayez d'écrire un article sur les sections CDATA. Votre astuce ordinaire consistant à intégrer des échantillons de code dans une section CDATA vous échouera ici. Vous pouvez imaginer comment j'ai appris cela.

Mais dans la plupart des cas, vous ne rencontrerez pas cela, et voici pourquoi: si vous voulez stocker (par exemple) le texte d'un document XML comme contenu d'un élément XML, vous utiliserez probablement une méthode DOM, par exemple:

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

Et le DOM échappe assez raisonnablement le <et le>, ce qui signifie que vous n'avez pas incorporé par inadvertance une section CDATA dans votre document.

Oh, et c'est intéressant:

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);

C'est probablement une idéosyncrasie du DOM .NET, mais cela ne lève pas d'exception. L'exception est levée ici:

Console.Write(doc.OuterXml);

Je suppose que ce qui se passe sous le capot, c'est que le XmlDocument utilise un XmlWriter pour produire sa sortie, et le XmlWriter vérifie la bonne formation au fur et à mesure qu'il écrit.

Robert Rossney
la source
Eh bien, j'ai eu un exemple presque "réel". Je charge généralement du XML à partir de Flash qui contient du balisage html dans les sections CDATA. Avoir un moyen de s'en échapper pourrait être utile, je suppose. Mais de toute façon, dans ce cas, le contenu CDATA est généralement du XHTML valide, et ainsi le CDATA "externe" pourrait être évité complètement.
Juan Pablo Califano
2
CDATA peut presque toujours être complètement évité. Je trouve que les gens qui luttent très souvent avec CDATA ne comprennent pas ce qu'ils essaient vraiment de faire et / ou comment la technologie qu'ils utilisent fonctionne vraiment.
Robert Rossney
Oh, je devrais aussi ajouter que la seule raison pour laquelle le CMS auquel j'ai fait allusion dans ma réponse utilisait CDATA était que je l'ai écrit et que je ne comprenais pas ce que j'essayais vraiment de faire et / ou comment la technologie fonctionne. Je n'avais pas besoin d'utiliser CDATA.
Robert Rossney
Si vous utilisez .net, le commentaire précédent sur le fait que CDATA est évitable est parfait - écrivez simplement le contenu sous forme de chaîne et le framework fera tout l'échappement (et sans échappement en lecture) pour vous du monde réel ... ... xmlStream.WriteStartElement ("UnprocessedHtml"); xmlStream.WriteString (UnprocessedHtml); xmlStream.WriteEndElement ();
Mark Mullin
6

remplacez simplement ]]>par]]]]><![CDATA[>

Thomas Grainger
la source
3

Voici un autre cas dans lequel ]]>doit être échappé. Supposons que nous devions enregistrer un document HTML parfaitement valide dans un bloc CDATA d'un document XML et que la source HTML possède son propre bloc CDATA. Par exemple:

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

le suffixe CDATA commenté doit être changé en:

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

puisqu'un analyseur XML ne saura pas comment gérer les blocs de commentaires javascript

Shawn Becker
la source
Ce n'est pas un cas particulier. Remplacer simplement ]]>par ]]]]><![CDATA[>s'applique toujours ici. Le fait que ce soit JavaScript ou commenté n'est pas important.
Thomas Grainger
1

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

user2194495
la source
1

Une manière plus propre en PHP:

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

N'oubliez pas d'utiliser un str_replace sécurisé multi-octets si nécessaire (non latin1 $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;
   }
Alain Tiemblo
la source
Pouvez-vous expliquer votre vote défavorable? Dire que j'ai fait une erreur n'est pas aussi utile que d'expliquer où elle se trouve.
Alain Tiemblo
Il n'est pas nécessaire d'effectuer un remplacement sécurisé multi-octets si vous utilisez UTF-8. Je n'ai pas voté contre :)
frodeborli
-1

Je ne pense pas que l'interruption de CDATA soit une bonne solution. Voici mon alternative ...

Utilisez ]pour la séquence d'échappement suivie de la valeur hexadécimale de votre personnage. Comme dans le &#xhhhh;=>]<unicode value>;

De cette façon, si vous essayez d'enregistrer ]]>votre encodage fn produira ]005D;]005D;]003E;ce qui est ok dans CDATA.

C'est mieux que de s'échapper par nom d'entité, car ceux-ci ne sont pas décodés à chaque fois dans votre application et vous pouvez avoir des priorités différentes pour échapper les entités avec esperluette vs échapper à d'autres caractères / séquences. En conséquence, vous avez plus de contrôle sur le contenu de CDATA.

honzar
la source
-2

Voir cette structure:

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

Pour les balises CDATA internes, vous devez fermer avec ]]]]><![CDATA[>au lieu de ]]>. Aussi simple que cela.

Chad Kuehn
la source