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 ...


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>]]> 

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 >.


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.


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.


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


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


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

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

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.


-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.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.