Czy istnieje sposób na uniknięcie tokena końcowego CDATA w XML?

133

Zastanawiałem się, czy istnieje sposób na uniknięcie tokena końcowego CDATA ( ]]>) w sekcji CDATA w dokumencie xml. Lub, bardziej ogólnie, jeśli istnieje jakaś sekwencja ucieczki do użycia w CDATA (ale jeśli istnieje, myślę, że prawdopodobnie i tak miałaby sens tylko ucieczka od tokenów początku lub końca).

Zasadniczo, czy możesz mieć token początku lub końca osadzony w CDATA i powiedzieć parserowi, aby go nie interpretował, ale traktował jako kolejną sekwencję znaków.

Prawdopodobnie powinieneś po prostu refaktoryzować swoją strukturę xml lub kod, jeśli próbujesz to zrobić, ale mimo że pracuję z xml na co dzień przez ostatnie 3 lata i nigdy nie miałem tego problemu, Zastanawiałem się, czy to możliwe. Z czystej ciekawości.

Edytować:

Poza kodowaniem html ...

Juan Pablo Califano
źródło
4
Po pierwsze, przyjmuję odpowiedź jako poprawną, ale uwaga: nic nie uniemożliwia komuś kodowania, >jak >w przypadku CData, aby zapewnić, że elementy osadzone ]]>nie zostaną przeanalizowane jako CDEnd. Oznacza to po prostu, że jest to nieoczekiwane i &musi NAJPIERW zostać zakodowane, &aby dane mogły zostać prawidłowo zdekodowane. Użytkownicy dokumentu muszą wiedzieć, aby zdekodować również te dane CD. Nie jest to niespotykane, ponieważ częścią CData jest przechowywanie treści, z którymi określony konsument rozumie, jak sobie z nimi radzić. Po prostu nie można oczekiwać, że taka CData będzie właściwie interpretowana przez żadnego ogólnego konsumenta.
nix
1
@nix, CDATA zapewnia po prostu jawny sposób zadeklarowania zawartości węzła tekstowego w taki sposób, że tokeny językowe w obrębie (inne niż]]>) nie są analizowane. W szczególności nie rozszerza odniesień do jednostek, takich jak & gt; z tego powodu, więc w bloku CDATA, oznacza to tylko te cztery znaki, a nie „>”. Ujmując to z innej perspektywy: w specyfikacji XML cała zawartość tekstowa nazywana jest „cdata”, a nie tylko te sekwencje („dane znaków”). Nie chodzi też o konkretne środki konsumujące. (Taka rzecz jednak istnieje - instrukcje przetwarzania (<? Target instrukcja?>).
Średnik,
(Powinienem dodać, nawet jeśli tego rodzaju rzeczy są sprzeczne z pierwotnymi intencjami węzła, wszystko jest sprawiedliwe w długiej i męczącej walce z XMLem. Po prostu czuję, że czytelnicy powinni wiedzieć, że <! [CDATA [ ]]> właściwie nie został zaprojektowany do tego celu.)
Średnik,
1
@Semicolon CDATAzostał zaprojektowany tak, aby zezwalać na wszystko : są one używane do zmiany znaczenia bloków tekstu zawierających znaki, które w przeciwnym razie zostałyby rozpoznane jako znaczniki. To CDATArównież sugeruje, ponieważ jest to również znacznik. Ale w rzeczywistości nie potrzebujesz podwójnego kodowania, które zasugerowałem. ]]&gt;jest dopuszczalnym sposobem kodowania a CDEndw CDATA.
nix
To prawda, że ​​nie potrzebujesz podwójnego kodowania - ale nadal będziesz potrzebować agenta, aby miał specjalną wiedzę, ponieważ parser nie przeanalizowałby & gt; jako>. Myślę, że to masz na myśli? Że możesz je wymienić według własnego uznania po przeanalizowaniu?
Średnik

Odpowiedzi:

141

Oczywiście to pytanie jest czysto akademickie. Na szczęście ma bardzo określoną odpowiedź.

Nie można uciec przed sekwencją końcową CDATA. Reguła produkcji 20 specyfikacji XML jest dość jasna:

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

EDYCJA: Ta reguła iloczynu dosłownie oznacza „Sekcja CData może zawierać wszystko, co chcesz, ALE sekwencja ']]>'. Bez wyjątku.”.

EDYCJA2: ta sama sekcja czyta również:

W sekcji CDATA tylko łańcuch CDEnd jest rozpoznawany jako znacznik, więc lewe nawiasy ostre i ampersandy mogą występować w ich dosłownej formie; nie trzeba (i nie można) przed nimi uciec za pomocą " &lt;" i "&amp; ”. Sekcje CDATA nie mogą zagnieżdżać się.

Innymi słowy, nie jest możliwe użycie odwołania do encji, znaczników ani żadnej innej formy interpretowanej składni. Jedyny przeanalizowany tekst w sekcji CDATA to]]> i kończy sekcję.

Dlatego nie można uciec ]]> z sekcji CDATA.

EDYCJA3: ta sama sekcja czyta również:

2.7 Sekcje CDATA

[Definicja: Sekcje CDATA mogą występować wszędzie tam, gdzie mogą wystąpić dane znakowe; służą do zmiany znaczenia bloków tekstu zawierających znaki, które w innym przypadku zostałyby rozpoznane jako znaczniki. Sekcje CDATA zaczynają się od ciągu „<! [CDATA [” i kończą się ciągiem „]]>”:]

Wtedy może istnieć sekcja CDATA wszędzie tam, gdzie mogą wystąpić dane znakowe, w tym wiele sąsiednich sekcji CDATA zamiast jednej sekcji CDATA. Dzięki temu możliwe jest podzielenie ]]>tokena i umieszczenie jego dwóch części w sąsiednich sekcjach CDATA.

dawny:

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

należy zapisać jako

<![CDATA[Certain tokens like ]]]]><![CDATA[> can be difficult and <valid>]]> 
ddaa
źródło
1
W rzeczy samej. Cóż, nie jestem typem akademickim, ale jak powiedziałem w pytaniu, jestem tego po prostu ciekawy. Szczerze mówiąc, wierzę ci na słowo, ponieważ ledwo rozumiem składnię używaną w regule. Dzięki za odpowiedź.
Juan Pablo Califano
40
To nie jest kwestia akademicka. Pomyśl o kanale RSS postu na blogu, który zawiera dyskusję na temat CDATA.
usr
4
Miałem na myśli „akademicki” w tym sensie: „interesujący do dyskusji, ale bez praktycznego zastosowania”. Ogólnie rzecz biorąc, CDATA nie jest użyteczne, jest po prostu sposobem serializacji tekstu XML i jest semantycznie równoważne z unikaniem znaków specjalnych przy użyciu jednostek znakowych & lt; & gt; i & quot ;. Jednostki znaków są najprostszym, najbardziej niezawodnym i najbardziej ogólnym rozwiązaniem, więc używaj go zamiast sekcji CDATA. Jeśli używasz odpowiedniej biblioteki XML (zamiast budować XML z łańcuchów), nie musisz nawet o tym myśleć.
ddaa,
5
Właśnie zostałem ugryziony, ponieważ próbuję zakodować skompresowany kod JavaScript w znaczniku <script>, na przykład: <script>/*<![CDATA[*/javascript goes here/*]]>*/</script>a mój javascript zawiera tylko tę sekwencję! Podoba mi się pomysł podziału na wiele sekcji CDATA ...
NickZoic
3
Doświadczyłem tego w prawdziwym świecie. Czytając zrzut z Wikipedii i pisząc kolejny plik xml, napotkałem to na stronie National Transportation Safety Board . Zawierał on > 100 milionów dolarów (2013) do budżetu w infoboksie. Zawarty plik źródłowy xml, na [[United States dollar|US$]]&gt;100 million (2013)który został przetłumaczony [[United States dollar|US$]]>100 million (2013)przez czytelnika, a autor zdecydował się na użycie CDATA w celu uniknięcia tekstu, ale nie powiodło się.
Paul Jackson
170

Musisz rozbić swoje dane na części, aby ukryć ]]> .

Oto cała sprawa:

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

Pierwsza <![CDATA[]]]]>ma ]]. Drugi <![CDATA[>]]>ma >.

S.Lott
źródło
1
Dzięki za odpowiedź. Raczej szukałem czegoś w rodzaju odpowiednika z ukośnikiem odwrotnym (w łańcuchach w C, PHP, Java itp.). Zgodnie z zasadą cytowaną przez ddaa wydaje się, że czegoś takiego nie ma.
Juan Pablo Califano
28
To powinna być akceptowana odpowiedź. Ucieczka jest terminem nieco niejednoznacznym, ale ta odpowiedź zdecydowanie odnosi się do ducha ucieczki . Szkoda, że ​​nie pasuje to do wąskiej koncepcji ucieczki PO , która z jakiegoś powodu arbitralnie wymaga użycia znaku ukośnika odwrotnego.
G-Wiz,
5
Podsumowując, uciekaj ]]>jako ]]]]><![CDATA[>. 5 razy dłuższa ... wow. Ale jest to niecodzienna sekwencja.
Brilliand
5
Nie tylko 5-krotna długość jest zabawna, ale nie jest to nawet rzadka sekwencja w kodzie, która jest głównym przypadkiem użycia CDATA! Zakładając skompresowany JavaScript, który usuwa spacje, możesz uzyskiwać dostęp do pola według nazwy z tablicy nazw według indeksu, na przykład „if (fields [fieldnames [0]]> 3)”, a teraz musisz zmienić to na „if ( pola [nazwy pól [0]]]]> <! [CDATA [> 3) ", co jest sprzeczne z celem używania CDATA, aby uczynić go bardziej czytelnym, LOL. Chciałbym ustnie uderzyć każdego, kto wymyślił składnię CDATA.
Triynko
1
Escaping, a dokładniej cytowanie, oznacza wstawienie tekstu w kontekście, w którym tekst surowy ma znaczenie BEZ opuszczania kontekstu. Nie ma to nic wspólnego z ukośnikami odwrotnymi. Ta odpowiedź nie jest ucieczką ani cytowaniem, ponieważ tworzy dwie sekcje CDATA zamiast jednej.
ddaa
17

Nie uciec ]]>, ale można uciec >po ]]wstawiając ]]><![CDATA[przed >, myśleć o tym po prostu jak \w C / Java / PHP / Perl ciąg ale potrzebne tylko przed >i po ]].

BTW,

Odpowiedź S. Lotta jest taka sama, tylko inaczej sformułowana.

Jason Pyeron
źródło
2
Wolę to sformułowanie. :)
Brilliand
3
Ten sposób mówienia daje ludziom zły pomysł. To nie ucieka. ]]]]><![CDATA[>to nie jest jakaś magiczna sekwencja ]]>. ]]]]>zawiera ]]znaki jako dane i ]]>kończy bieżącą sekcję CDATA. <![CDATA[>uruchamia nową sekcję CDATA i umieszcza >w niej. W rzeczywistości są to dwa różne elementy i będą traktowane inaczej podczas pracy z parserem DOM. Powinieneś być tego świadomy. Ten sposób jest podobny do tego ]]]><![CDATA[]>, z wyjątkiem tego, że umieszcza ]w pierwszym i ]>drugim CDATA. Różnica pozostaje.
Aidiakapi
Różnica jest zawyżona, ponieważ zawartość CDATA jest traktowana jako dosłowny zakres tekstu uciekającego. Tylko wtedy, gdy miesza się z DOM, ma to naprawdę znaczenie, a na tym poziomie i tak masz do czynienia z innymi niewidocznymi granicami, takimi jak tekst, komentarze i węzły instrukcji przetwarzania.
Beejor
7

Odpowiedź S. Lotta jest prawidłowa: nie kodujesz znacznika końcowego, dzielisz go na wiele sekcji CDATA.

Jak rozwiązać ten problem w prawdziwym świecie: używając edytora XML do stworzenia dokumentu XML, który zostanie wprowadzony do systemu zarządzania treścią, spróbuj napisać artykuł o sekcjach CDATA. Twoja zwykła sztuczka polegająca na osadzaniu próbek kodu w sekcji CDATA zawiedzie Cię tutaj. Możesz sobie wyobrazić, jak się tego nauczyłem.

Ale w większości przypadków nie napotkasz tego, a oto dlaczego: jeśli chcesz przechowywać (powiedzmy) tekst dokumentu XML jako zawartość elementu XML, prawdopodobnie użyjesz metody DOM, np:

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

A DOM całkiem rozsądnie wymyka się znakom <i>, co oznacza, że ​​nieumyślnie nie osadziłeś sekcji CDATA w swoim dokumencie.

Aha, i to jest interesujące:

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

Jest to prawdopodobnie przypadek ideologii .NET DOM, ale nie stanowi to wyjątku. Wyjątek zostanie zgłoszony tutaj:

Console.Write(doc.OuterXml);

Domyślam się, że to, co dzieje się pod maską, polega na tym, że XmlDocument używa XmlWriter do tworzenia swoich danych wyjściowych, a XmlWriter sprawdza, czy podczas pisania jest poprawna.

Robert Rossney
źródło
Cóż, miałem przykład niemal z „prawdziwego świata”. Zwykle ładuję XML z Flasha, który zawiera znaczniki HTML w sekcjach CDATA. Myślę, że znalezienie sposobu na ucieczkę może się przydać. W każdym razie w takim przypadku zawartość CDATA jest zwykle prawidłowym XHTML, więc można by całkowicie uniknąć "zewnętrznego" CDATA.
Juan Pablo Califano
2
Prawie zawsze można całkowicie uniknąć CDATA. Uważam, że ludzie, którzy bardzo często zmagają się z CDATA, nie rozumieją, co tak naprawdę próbują zrobić i / lub jak naprawdę działa technologia, której używają.
Robert Rossney
Och, powinienem również dodać, że jedynym powodem, dla którego CMS, o którym wspomniałem w mojej odpowiedzi, był fakt, że go napisałem i nie rozumiałem, co tak naprawdę próbuję zrobić i / lub jak działa ta technologia. Nie musiałem używać CDATA.
Robert Rossney
Jeśli używasz .net, poprzedni komentarz dotyczący unikania CDATA jest na miejscu - po prostu zapisz zawartość jako ciąg, a framework wykona za Ciebie wszystkie znaki ucieczki (i zmiany znaczenia przy odczycie) z prawdziwego świata ... ... xmlStream.WriteStartElement ("UnprocessedHtml"); xmlStream.WriteString (UnprocessedHtml); xmlStream.WriteEndElement ();
Mark Mullin
6

po prostu zastąpić ]]>z]]]]><![CDATA[>

Thomas Grainger
źródło
3

Oto kolejny przypadek, w którym ]]>należy uciec. Załóżmy, że musimy zapisać doskonale poprawny dokument HTML wewnątrz bloku CDATA dokumentu XML, a tak się składa, że ​​źródło HTML ma swój własny blok CDATA. Na przykład:

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

skomentowany sufiks CDATA należy zmienić na:

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

ponieważ parser XML nie będzie wiedział, jak obsługiwać bloki komentarzy javascript

Shawn Becker
źródło
To nie jest przypadek specjalny. Wystarczy wymienić ]]>ze ]]]]><![CDATA[>nadal obowiązuje tutaj. Fakt, że jest to JavaScript lub skomentowany, nie jest ważny.
Thomas Grainger
1

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

user2194495
źródło
1

Czystszy sposób w PHP:

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

Nie zapomnij użyć wielobajtowego bezpiecznego str_replace, jeśli jest to wymagane (inne niż 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
źródło
Czy możesz wyjaśnić swój sprzeciw? Powiedzenie, że popełniłem błąd, nie jest tak przydatne, jak wyjaśnienie, gdzie on jest.
Alain Tiemblo
Nie ma potrzeby wykonywania bezpiecznej wielobajtowej wymiany, jeśli używasz UTF-8. Nie głosowałem jednak przeciw :)
frodeborli
-1

Nie sądzę, aby przerywanie CDATA było dobrym rozwiązaniem. Oto moja alternatywa ...

Użyj ]dla sekwencji ucieczki, po której następuje wartość szesnastkowa twojej postaci. Jak w &#xhhhh;=>]<unicode value>;

W ten sposób, jeśli spróbujesz nagrać ]]>swoje kodowanie, fn da, ]005D;]005D;]003E;co jest w porządku w CDATA.

Jest to lepsze niż ucieczka przez nazwę jednostki, ponieważ nie są one dekodowane za każdym razem w aplikacji i możesz mieć inne priorytety ucieczki jednostek za pomocą znaku ampersand i ucieczki przed niektórymi innymi znakami / sekwencjami. W rezultacie masz większą kontrolę nad zawartością CDATA.

Honzar
źródło
-2

Zobacz tę strukturę:

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

W przypadku wewnętrznych znaczników CDATA należy zamknąć za pomocą ]]]]><![CDATA[>zamiast ]]>. Proste.

Chad Kuehn
źródło