Je me demandais s'il existe un moyen d'échapper à un jeton de fin CDATA (]]>
) dans une section CDATA d'un document xml. Ou, plus généralement, s'il existe une séquence d'échappement à utiliser dans un CDATA (mais si elle existe, j'imagine qu'il serait probablement logique de sortir uniquement du début ou de la fin des jetons).
En gros, pouvez-vous incorporer un jeton de début ou de fin dans un CDATA et dire à l'analyseur syntaxique de ne pas l'interpréter, mais de le traiter simplement 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 avec xml quotidiennement depuis environ 3 ans et que je n’ai jamais eu ce problème, Je me demandais si c'était possible. Juste par curiosité.
Modifier:
Autre que l'utilisation de l'encodage HTML ...
Clairement, cette question est purement académique. Heureusement, la réponse est très claire.
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 se lit également:
Dans une section CDATA, seule la chaîne CDEnd est reconnue en tant que balisage, de sorte que les crochets angulaires et les esperluettes à gauche peuvent apparaître dans leur forme littérale; ils ne doivent pas (et ne peuvent pas) être échappés avec "
<
" et "&
". Les sections CDATA ne peuvent pas imbriquer.
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é dans une section CDATA est ]]>
et termine la section.
Par conséquent, il n'est pas possible d'échapper à ]]>
dans une section CDATA.
EDIT3: La même section se lit également:
2.7 Sections CDATA
[Définition: les sections CDATA peuvent apparaître n'importe 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ère 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 ses deux parties 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>]]>
Vous devez fractionner vos données pour dissimuler le ]]>
.
Voici le tout:
<![CDATA[]]]]><![CDATA[>]]>
Le premier <![CDATA[]]]]>
a le ]]
. Le second <![CDATA[>]]>
a le >
.
Vous n'échappez pas au ]]>
mais vous échappez au >
après le ]]
en insérant ]]><![CDATA[
avant le >
, pensez à cela comme un \
dans la chaîne C/Java/PHP/Perl, mais nécessaire avant un >
et après un ]]
.
BTW,
La réponse de S.Lott est la même que celle-ci, mais libellée différemment.
La réponse de S. Lott est exacte: vous ne codez pas la balise de fin, vous la découpez dans plusieurs sections CDATA.
Comment gérer 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 à incorporer des exemples de code dans une section CDATA vous fera échouer 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 souhaitez stocker (par exemple) le texte d'un document XML en tant que 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 à <et>, 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 jette pas une exception. L'exception est levée ici:
Console.Write(doc.OuterXml);
J'imagine que ce qui se passe sous le capot est que XmlDocument utilise un XmlWriter pour produire sa sortie et que XmlWriter vérifie le bon format lors de son écriture.
remplacez simplement ]]>
par ]]]]><![CDATA[>
Voici un autre cas dans lequel ]]>
doit être évité. Supposons que nous ayons besoin de sauvegarder un document HTML parfaitement valide dans un bloc CDATA d'un document XML et que la source HTML ait 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 remplacé par:
/* ]]]]><![CDATA[> *//
puisqu'un analyseur XML ne va pas savoir comment gérer les blocs de commentaires javascript
En PHP: '<![CDATA['.implode(explode(']]>', $string), ']]]]><![CDATA[>').']]>'
Une manière plus propre en PHP:
function safeCData($string)
{
return '<![CDATA[' . str_replace(']]>', ']]]]><![CDATA[>', $string) . ']]>';
}
N'oubliez pas d'utiliser un str_replace multibyte-safe 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;
}
Une autre solution consiste à remplacer ]]>
par ]]]><![CDATA[]>
.
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.