Gibt es eine Möglichkeit, ein CDATA-End-Token in XML zu umgehen?

128

Ich habe mich gefragt, ob es eine Möglichkeit gibt, ein CDATA-End-Token ( ]]>) in einem CDATA-Abschnitt in einem XML-Dokument zu umgehen. Oder allgemeiner, wenn es eine Escape-Sequenz für die Verwendung innerhalb einer CDATA gibt (aber wenn sie existiert, wäre es wahrscheinlich nur sinnvoll, Anfangs- oder End-Token zu maskieren).

Grundsätzlich können Sie ein Start- oder End-Token in eine CDATA einbetten und den Parser anweisen, es nicht zu interpretieren, sondern es nur als eine andere Zeichenfolge zu behandeln.

Wahrscheinlich sollten Sie einfach Ihre XML-Struktur oder Ihren Code umgestalten, wenn Sie dies versuchen, aber obwohl ich in den letzten 3 Jahren täglich mit XML gearbeitet habe und dieses Problem nie hatte, Ich habe mich gefragt, ob es möglich ist. Nur aus Neugier.

Bearbeiten:

Anders als die Verwendung von HTML-Codierung ...

Juan Pablo Califano
quelle
4
Erstens akzeptiere ich die Antwort als richtig, aber beachte: Nichts hindert jemanden daran, >wie >in CData zu codieren, um sicherzustellen, dass Embedded ]]>nicht als CDEnd analysiert wird. Es bedeutet einfach, dass es unerwartet ist und dass &ZUERST auch codiert werden muss, &damit die Daten ordnungsgemäß decodiert werden können. Benutzer des Dokuments müssen wissen, dass sie auch diese CData dekodieren können. Dies ist nicht ungewöhnlich, da ein Teil des Zwecks von CData darin besteht, Inhalte zu enthalten, mit denen ein bestimmter Verbraucher umgehen kann. Es ist einfach nicht zu erwarten, dass eine solche CData von einem generischen Verbraucher richtig interpretiert wird.
Nix
1
@nix, CDATA bietet lediglich eine explizite Möglichkeit, Textknoteninhalte so zu deklarieren, dass Sprachtoken innerhalb (außer]]>) nicht analysiert werden. Entitätsreferenzen wie & gt; Aus diesem Grund bedeutet dies in einem CDATA-Block nur diese vier Zeichen, nicht '>'. Um es in die richtige Perspektive zu bringen: In der XML-Spezifikation wird der gesamte Textinhalt als "cdata" bezeichnet, nicht nur diese Sequenzen ("Zeichendaten"). Es geht auch nicht um bestimmte Konsumenten. (So ​​etwas gibt es allerdings - Verarbeitungsanweisungen (<? Zielanweisung?>).
Semikolon
(Ich sollte hinzufügen, auch wenn diese Art von Dingen der ursprünglichen Absicht des Knotens zuwiderläuft, ist im langen und qualvollen Kampf mit XML alles fair. Ich denke nur, dass es für Leser nützlich sein könnte, zu wissen, dass <! [CDATA [ ]]> wurde eigentlich nicht für diesen Zweck entwickelt.)
Semikolon
1
@Semicolon CDATAwurde entwickelt, um alles zuzulassen : Sie werden verwendet, um Textblöcken zu entkommen, die Zeichen enthalten, die ansonsten als Markup erkannt würden. Dies impliziert CDATAauch, da es sich auch um Markup handelt. Tatsächlich benötigen Sie jedoch nicht die von mir implizierte Doppelkodierung. ]]&gt;ist ein akzeptables Mittel zum Codieren von a CDEndinnerhalb von a CDATA.
Nix
Sie würden zwar keine doppelte Codierung benötigen - aber der Agent muss über spezielle Kenntnisse verfügen, da der Parser & gt; als>. Das meinst du aber, denke ich? Dass Sie sie nach dem Parsen nach Belieben ersetzen können?
Semikolon

Antworten:

140

Diese Frage ist eindeutig rein akademisch. Zum Glück hat es eine sehr eindeutige Antwort.

Sie können einer CDATA-Endsequenz nicht entkommen. Die Produktionsregel 20 der XML- Spezifikation ist ganz klar:

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

BEARBEITEN: Diese Produktregel bedeutet wörtlich "Ein CData-Abschnitt kann alles enthalten, was Sie wollen, ABER die Sequenz ']]>'. Keine Ausnahme.".

EDIT2: Der gleiche Abschnitt lautet auch:

Innerhalb eines CDATA-Abschnitts wird nur die CDEnd-Zeichenfolge als Markup erkannt, sodass linke spitze Klammern und kaufmännisches Und in ihrer wörtlichen Form auftreten können. Sie müssen (und können) nicht mit " &lt;" und " &amp;" maskiert werden . CDATA-Abschnitte können nicht verschachtelt werden.

Mit anderen Worten, es ist nicht möglich, Entitätsreferenzen, Markups oder andere Formen der interpretierten Syntax zu verwenden. Der einzige analysierte Text in einem CDATA-Abschnitt ist ]]>und beendet den Abschnitt.

Daher ist es nicht möglich, ]]>innerhalb eines CDATA-Abschnitts zu entkommen .

EDIT3: Der gleiche Abschnitt lautet auch:

2.7 CDATA-Abschnitte

[Definition: CDATA-Abschnitte können überall dort auftreten, wo Zeichendaten auftreten können. Sie werden verwendet, um Textblöcke zu umgehen, die Zeichen enthalten, die andernfalls als Markup erkannt würden. CDATA-Abschnitte beginnen mit der Zeichenfolge "<! [CDATA [" und enden mit der Zeichenfolge "]]>":]

Dann kann es überall dort, wo Zeichendaten auftreten können, einen CDATA-Abschnitt geben, einschließlich mehrerer benachbarter CDATA-Abschnitte anstelle eines einzelnen CDATA-Abschnitts. Auf diese Weise kann das ]]>Token aufgeteilt und die beiden Teile in benachbarte CDATA-Abschnitte eingefügt werden.

Ex:

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

sollte geschrieben werden als

<![CDATA[Certain tokens like ]]]]><![CDATA[> can be difficult and <valid>]]> 
ddaa
quelle
1
Tatsächlich. Nun, ich bin kein akademischer Typ, aber wie ich in der Frage sagte, bin ich nur neugierig darauf. Um ehrlich zu sein, nehme ich einfach Ihr Wort dazu, weil ich aus der für die Regel verwendeten Syntax kaum einen Sinn machen kann. Danke für deine Antwort.
Juan Pablo Califano
39
Dies ist keine akademische Frage. Denken Sie an einen RSS-Feed eines Blogposts, der eine Diskussion über CDATA enthält.
usr
4
Ich meinte "akademisch" im Sinne von "interessant zu diskutieren, aber ohne praktischen Nutzen". Im Allgemeinen ist CDATA nicht nützlich, es ist nur eine Möglichkeit, XML-Text zu serialisieren, und es ist semantisch äquivalent dazu, Sonderzeichen mithilfe von Zeichenentitäten & lt; & gt; und & quot;. Zeichenentitäten sind die einfachste, robusteste und allgemeinste Lösung. Verwenden Sie diese anstelle von CDATA-Abschnitten. Wenn Sie eine richtige XML-Bibliothek verwenden (anstatt XML aus Zeichenfolgen zu erstellen), müssen Sie nicht einmal darüber nachdenken.
Ddaa
5
Ich wurde gerade von diesem gebissen, weil ich versuche, etwas komprimiertes Javascript in ein <script> -Tag wie das zu codieren: <script>/*<![CDATA[*/javascript goes here/*]]>*/</script>und mein Javascript enthält genau diese Sequenz! Ich mag die Idee, in mehrere CDATA-Abschnitte
aufzuteilen
2
Ich habe das in der realen Welt erlebt. Beim Lesen des Wikipedia-Dumps und beim Schreiben einer weiteren XML-Datei bin ich auf der Seite des National Transportation Safety Board darauf gestoßen . Es enthielt > 100 Millionen US-Dollar (2013) für das Budget in der Infobox. Die enthaltene Quell-XML, in [[United States dollar|US$]]&gt;100 million (2013)die [[United States dollar|US$]]>100 million (2013)der Leser und der Verfasser übersetzt haben, entschied sich für die Verwendung von CDATA, um dem Text zu entkommen, und schlug fehl.
Paul Jackson
169

Sie müssen Ihre Daten in Teile zerlegen, um das zu verbergen ]]>.

Hier ist das Ganze:

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

Der erste <![CDATA[]]]]>hat die ]]. Der zweite <![CDATA[>]]>hat die >.

S.Lott
quelle
1
Danke für deine Antwort. Ich suchte eher nach einem Backslash-Äquivalent (innerhalb von Strings in C, PHP, Java usw.). Nach der von ddaa zitierten Regel scheint es so etwas nicht zu geben.
Juan Pablo Califano
28
Dies sollte die akzeptierte Antwort sein. Flucht ist ein etwas mehrdeutiger Begriff, aber diese Antwort spricht definitiv den Geist der Flucht an . Schade , dass es nicht der OP in dem enge Konzeption paßt die Flucht , die willkürlich den umgekehrten Schrägstrich erfordert aus irgendeinem Grunde zu beteiligen.
G-Wiz
5
Also zusammenfassend entkommen ]]>als ]]]]><![CDATA[>. 5 mal so lang ... wow. Aber dann ist es eine ungewöhnliche Sequenz.
Brilliand
5
Die 5-fache Länge ist nicht nur witzig, es ist auch keine ungewöhnliche Sequenz im Code, was der Hauptanwendungsfall von CDATA ist! Unter der Annahme, dass komprimiertes JavaScript Leerzeichen entfernt, könnten Sie auf ein Feld nach Namen aus einem Array von Namen nach Index zugreifen, z. B. "if (Felder [Feldnamen [0]]> 3)", und jetzt müssen Sie es in "if ( fields [fieldnames [0]]]]> <! [CDATA [> 3) ", was den Zweck der Verwendung von CDATA zur besseren Lesbarkeit zunichte macht, LOL. Ich möchte jeden, der sich die CDATA-Syntax ausgedacht hat, verbal schlagen.
Triynko
1
Das Entkommen oder korrekter das Zitieren bedeutet, dass Text in einen Kontext eingefügt wird, in dem der Rohtext eine Bedeutung hat, ohne den Kontext zu verlassen. Es hat nichts mit Backslashes zu tun. Und diese Antwort entgeht nicht und zitiert nicht, da sie zwei CDATA-Abschnitte anstelle von einem erzeugt.
ddaa
17

Sie haben nicht die entkommen , ]]>aber sie die entkommen , >nachdem ]]durch das Einfügen ]]><![CDATA[vor dem >, denken Sie an diesen ebenso wie ein \in C / Java / PHP / Perl - String , sondern nur vor einem benötigt >und nach einem ]].

Übrigens,

Die Antwort von S.Lott ist dieselbe, nur anders formuliert.

Jason Pyeron
quelle
2
Ich bevorzuge diesen Wortlaut. :)
Brilliand
3
Diese Art zu sagen gibt den Menschen die falsche Idee. Dies entgeht nicht . ]]]]><![CDATA[>ist keine magische Sequenz für ]]>. ]]]]>hat ]]Zeichen als Daten und ]]>beendet den aktuellen CDATA-Abschnitt. <![CDATA[>Startet einen neuen CDATA-Abschnitt und fügt >ihn ein. Sie sind eigentlich zwei verschiedene Elemente und werden bei der Arbeit mit einem DOM-Parser unterschiedlich behandelt. Sie sollten sich dessen bewusst sein. Diese Vorgehensweise ist ähnlich ]]]><![CDATA[]>, außer dass ]die erste und ]>die zweite CDATA eingefügt werden. Der Unterschied bleibt bestehen.
Aidiakapi
Der Unterschied ist überbewertet, da CDATA-Inhalte als wörtliche Spanne von maskiertem Text behandelt werden. Nur wenn Sie mit dem DOM herumspielen, ist das wirklich wichtig, und auf dieser Ebene haben Sie es sowieso mit anderen unsichtbaren Grenzen wie Text-, Kommentar- und Verarbeitungsanweisungsknoten zu tun.
Beejor
7

Die Antwort von S. Lott ist richtig: Sie codieren das End-Tag nicht, sondern teilen es in mehrere CDATA-Abschnitte auf.

So begegnen Sie diesem Problem in der realen Welt: Erstellen Sie mithilfe eines XML-Editors ein XML-Dokument, das in ein Content-Management-System eingespeist wird, und schreiben Sie einen Artikel über CDATA-Abschnitte. Ihr gewöhnlicher Trick, Codebeispiele in einen CDATA-Abschnitt einzubetten, schlägt hier fehl. Sie können sich vorstellen, wie ich das gelernt habe.

In den meisten Fällen tritt dies jedoch nicht auf, und hier ist der Grund: Wenn Sie den Text eines XML-Dokuments als Inhalt eines XML-Elements speichern (z. B.) möchten, verwenden Sie wahrscheinlich eine DOM-Methode, z.

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

Und das DOM entgeht dem <und> dem vernünftigerweise, was bedeutet, dass Sie nicht versehentlich einen CDATA-Abschnitt in Ihr Dokument eingebettet haben.

Oh, und das ist interessant:

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

Dies ist wahrscheinlich eine Ideosynkrasie des .NET-DOM, aber das löst keine Ausnahme aus. Die Ausnahme wird hier ausgelöst:

Console.Write(doc.OuterXml);

Ich würde vermuten, dass unter der Haube passiert, dass das XmlDocument einen XmlWriter verwendet, um seine Ausgabe zu erzeugen, und der XmlWriter beim Schreiben auf Wohlgeformtheit prüft.

Robert Rossney
quelle
Nun, ich hatte ein fast "reales" Beispiel. Normalerweise lade ich XML aus Flash, das HTML-Markup in CDATA-Abschnitten enthält. Es könnte nützlich sein, einen Ausweg zu finden, denke ich. In diesem Fall ist der CDATA-Inhalt jedoch normalerweise gültiges XHTML, sodass die "äußere" CDATA insgesamt vermieden werden kann.
Juan Pablo Califano
2
CDATA kann fast immer ganz vermieden werden. Ich finde, dass Menschen, die sehr häufig mit CDATA zu kämpfen haben, nicht verstehen, was sie wirklich versuchen und / oder wie die Technologie, die sie verwenden, wirklich funktioniert.
Robert Rossney
Oh, ich sollte auch hinzufügen, dass der einzige Grund, warum das CMS, auf das ich in meiner Antwort anspielte, CDATA verwendete, darin bestand, dass ich es geschrieben habe und nicht verstand, was ich wirklich versuchte und / oder wie die Technologie funktioniert. Ich musste kein CDATA verwenden.
Robert Rossney
Wenn Sie .net verwenden, ist der vorstehende Kommentar, dass CDATA vermeidbar ist, genau richtig. Schreiben Sie einfach den Inhalt als Zeichenfolge, und das Framework erledigt das Entkommen (und Entweichen beim Lesen) für Sie aus der realen Welt. ... xmlStream.WriteStartElement ("UnprocessedHtml"); xmlStream.WriteString (UnprocessedHtml); xmlStream.WriteEndElement ();
Mark Mullin
6

ersetzen Sie einfach ]]>mit]]]]><![CDATA[>

Thomas Grainger
quelle
3

Hier ist ein weiterer Fall, in dem ]]>entkommen muss. Angenommen, wir müssen ein perfekt gültiges HTML-Dokument in einem CDATA-Block eines XML-Dokuments speichern, und die HTML-Quelle verfügt zufällig über einen eigenen CDATA-Block. Beispielsweise:

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

Das kommentierte CDATA-Suffix muss geändert werden in:

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

da ein XML-Parser nicht weiß, wie man mit Javascript-Kommentarblöcken umgeht

Shawn Becker
quelle
Dies ist kein Sonderfall. Ersetzen Sie einfach ]]>mit ]]]]><![CDATA[>noch hier gilt. Die Tatsache, dass es sich um JavaScript handelt oder kommentiert wird, ist nicht wichtig.
Thomas Grainger
1

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

user2194495
quelle
1

Ein sauberer Weg in PHP:

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

Vergessen Sie nicht, bei Bedarf einen multibyte-sicheren str_replace zu verwenden (nicht 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
quelle
Können Sie Ihre Ablehnung erklären? Zu sagen, dass ich einen Fehler gemacht habe, ist nicht so nützlich wie zu erklären, wo er ist.
Alain Tiemblo
Wenn Sie UTF-8 verwenden, müssen Sie kein Multibyte-sicheres Ersetzen durchführen. Ich habe aber nicht abgelehnt :)
frodeborli
-1

Ich denke nicht, dass es ein guter Weg ist, CDATA zu unterbrechen. Hier ist meine Alternative ...

Verwenden Sie diese Option ]für die Escape-Sequenz, gefolgt vom Hex-Wert Ihres Charakters. Wie im &#xhhhh;=>]<unicode value>;

Auf diese Weise wird, wenn Sie versuchen, ]]>Ihre Codierung aufzuzeichnen, fn erzeugt, ]005D;]005D;]003E;was in CDATA in Ordnung ist.

Es ist besser, als nach Entitätsnamen zu entkommen, da diese nicht jedes Mal in Ihrer App dekodiert werden und Sie möglicherweise andere Prioritäten für das Entkommen von Entitäten mit kaufmännischem Und haben als für das Entkommen anderer Zeichen / Sequenzen. Dadurch haben Sie mehr Kontrolle über den Inhalt von CDATA.

Honzar
quelle
-2

Siehe diese Struktur:

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

Für die inneren CDATA-Tags müssen Sie ]]]]><![CDATA[>statt mit schließen ]]>. So einfach ist das.

Chad Kuehn
quelle