Чи є спосіб вийти з маркера кінця CDATA у xml?


129

Мені було цікаво, чи є якийсь спосіб уникнути маркера закінчення CDATA ( ]]>) у розділі CDATA у документі xml. Або, загалом, якщо є якась послідовність запуску для використання в межах CDATA (але, якщо вона існує, я думаю, це, мабуть, має сенс лише бігти починаючи або закінчуючи лексеми, все одно).

В основному, чи можете ви мати маркер початку або кінця, вбудований в CDATA, і сказати аналізатору не інтерпретувати його, а трактувати його як лише іншу послідовність символів.

Можливо, вам слід просто переробити структуру xml або свій код, якщо ви виявите, що намагаєтесь це зробити, але, хоча я працюю з xml щодня протягом останніх 3 років або близько того, і я ніколи не мав цієї проблеми, Мені було цікаво, чи це можливо. Тільки з цікавості.

Редагувати:

Крім використання кодування html ...


4
По-перше, я приймаю відповідь як правильну, але зауважте: ніщо не заважає комусь кодувати >як >у CData, щоб переконатися, що вбудований ]]>не буде розбиратися як CDEnd. Це просто означає, що це несподівано, і &ПЕРШИЙ слід закодувати як &занадто, щоб дані можна було правильно розшифрувати. Користувачі документа також повинні знати, як розшифрувати цей CData. Це не нечувано, оскільки частина мети CData - це вміст, який конкретний споживач розуміє, як поводитися. Такого CData просто не можна очікувати, що будь-який загальний споживач інтерпретуватиме належним чином.
nix

1
@nix, CDATA просто надає явний спосіб оголосити вміст текстового вузла таким чином, що мовні лексеми в межах (крім]]>) не розбираються. Він спеціально не розширює посилання на об'єкти типу & gt; тому в блоці CDATA це означає лише чотири символи, а не '>'. Якщо говорити про це в перспективі: у специфікації xml весь текстовий вміст називається "cdata", а не лише ці послідовності ("data data"). Крім того, мова не йде про конкретні споживаючі засоби. (Хоча така річ існує - інструкції з обробки (<? Цільова інструкція?>).
Точка з комою

(Додам, навіть якщо подібні речі суперечать первісному наміру вузла, все справедливо в тривалій і покрутистій битві з XML. Я просто вважаю, що читачам може бути корисно знати, що <! [CDATA [ ]]> насправді не призначений для цієї мети.)
крапка з комою

1
@Semicolon CDATAбув розроблений таким чином, щоб дозволити що завгодно : вони використовуються для виходу блоків тексту, що містять символи, які в іншому випадку будуть розпізнані як розмітка, що також передбачає, CDATAоскільки це також розмітка. Але насправді вам не потрібно подвійне кодування, яке я мав на увазі. ]]&gt;є прийнятним засобом кодування в CDEndмежах a CDATA.
nix

Щоправда, вам не потрібно буде подвійне кодування - але вам все одно знадобиться агент, щоб мати спеціальні знання, оскільки аналізатор не буде розбирати & gt; як>. Це ви хочете, хоча, я думаю? Щоб ви могли замінити їх, як вважаєте за потрібне, після розбору?
напівколонка

Відповіді:


141

Зрозуміло, що це питання суто академічне. На щастя, він має дуже певну відповідь.

Ви не можете уникнути послідовності завершення CDATA. Правило виробництва 20 специфікації XML є досить зрозумілим:

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

EDIT: Це правило продукту буквально означає "Розділ CData може містити все, що завгодно, АЛЕ послідовність"]]> ". Не виняток."

EDIT2: У цьому ж розділі також написано:

У розділі CDATA тільки розмітка CDEnd розпізнається як розмітка, так що ліві кутові дужки та амперсанд можуть виникати в їх буквальному вигляді; їх не потрібно (і неможливо) уникнути, використовуючи " &lt;" і " &amp;". Розділи CDATA не можуть гніздитися.

Іншими словами, використовувати посилання, розмітку чи будь-яку іншу форму інтерпретованого синтаксису неможливо. Єдиний проаналізований текст у розділі CDATA є ]]>, і він закінчує розділ.

Отже, не вдається вийти ]]>з розділу CDATA.

EDIT3: У цьому ж розділі також написано:

2.7 Розділи CDATA

[Визначення: розділи CDATA можуть виникати будь-де, де можуть виникати дані символів; вони використовуються для виведення блоків тексту, що містять символи, які в іншому випадку будуть розпізнані як розмітка. Розділи CDATA починаються з рядка "<! [CDATA [" і закінчуються рядком "]]>":]

Тоді може бути розділ CDATA, де б не траплялися дані символів, включаючи кілька сусідніх розділів CDATA замість одного розділу CDATA. Це дозволяє розділити ]]>маркер і розмістити дві його частини в сусідніх розділах CDATA.

колишній:

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

слід писати як

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

1
Справді. Ну, я не академічний тип, але, як я вже говорив у запитанні, мені просто цікаво про це. Якщо чесно, я просто візьму ваше слово з цього приводу, тому що я ледве має сенсу з синтаксису, який використовується для правила. Дякую за вашу відповідь.
Хуан Пабло Каліфано

39
Це не академічне питання. Подумайте про RSS-канал публікації блогу, який містить дискусію про CDATA.
usr

4
Я мав на увазі «академічний» у сенсі: «цікавий для обговорення, але без практичного використання». Як правило, CDATA не є корисним, це лише спосіб серіалізації тексту XML, і це семантично еквівалентно уникненню спеціальних символів за допомогою сутності символів & lt; & gt; і & quot ;. Суб'єкти символів - це найпростіше, найміцніше і найзагальніше рішення, тому використовуйте це замість розділів CDATA. Якщо ви використовуєте належну бібліотеку XML (замість того, щоб будувати XML з рядків), вам навіть не доведеться думати про це.
ddaa

5
Мене просто покусав цей, бо я намагаюся кодувати якийсь стислий Javascript у тег <script> на кшталт: <script>/*<![CDATA[*/javascript goes here/*]]>*/</script>а мій JavaScript включає саме таку послідовність! Мені подобається ідея поділу на декілька розділів CDATA ...
NickZoic

3
Я пережив це в реальному світі. Під час читання дампів wikipedia та написання іншого файлу xml я зіткнувся з цим на сторінці Національної ради з безпеки транспорту . Він містив > 100 мільйонів доларів США (2013) для бюджету в інформаційному полі. Джерело xml, [[United States dollar|US$]]&gt;100 million (2013)яке містилося в перекладі [[United States dollar|US$]]>100 million (2013)читача та письменника, вирішило використовувати CDATA, щоб уникнути тексту і не вдалося.
Пол Джексон

169

Ви повинні розбити свої дані на частини, щоб приховати ]]>.

Ось вся справа:

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

Перший <![CDATA[]]]]>має ]]. Другий <![CDATA[>]]>має >.


1
Дякую за вашу відповідь. Я скоріше шукав щось на зразок зворотного косого еквівалента (в межах рядків на C, PHP, Java тощо). Згідно з правилом, яке цитує ddaa, схоже, що такого немає.
Хуан Пабло Каліфано

28
Це має бути прийнятою відповіддю. Втеча - дещо неоднозначне поняття, але ця відповідь, безумовно, стосується духу втечі . Шкода, що це не відповідає вузькій концепції OP про втечу , яка довільно вимагає, щоб певний характер був включений символ зворотної косої риси.
G-Wiz

5
Отже, підсумовуючи, втеча ]]>як ]]]]><![CDATA[>. У 5 разів більше довжини ... ух. Але тоді це непоодинока послідовність.
Brilliand

5
Мало того, що 5-кратна довжина є смішною, це навіть не рідкість послідовності в коді, що є основним випадком використання CDATA! Якщо припустити стиснутий JavaScript, який видаляє пробіли, ви можете отримати доступ до поля за назвою з масиву імен за індексом, наприклад, "якщо (поля [імена полів [0]]> 3)" ", і тепер вам доведеться змінити його на" if ( поля [імена полів [0]]]]> <! [CDATA [> 3) ", які перемагають призначення використання CDATA, щоб зробити його більш читабельним, LOL. Мені б хотілося словесно плескати того, хто придумав синтаксис CDATA.
Трайнко

1
Вимкнення або, правильніше, цитування, означає вставлення деякого тексту в контекст, де необроблений текст має значення БЕЗ виходу з контексту. Це не має нічого спільного з накидами. І ця відповідь не залишається без цитування, оскільки вона створює два розділи CDATA замість одного.
ddaa

17

Ви не уникаєте, ]]>але уникаєте >після ]], вставляючи ]]><![CDATA[перед> , думайте про це так само, як \у рядку C / Java / PHP / Perl, але потрібно лише перед a >і після a ]].

До речі,

Відповідь С.Лотта така ж, як і ця, лише сформульована по-різному.


2
Я вважаю за краще це формулювання. :)
Brilliand

3
Такий спосіб сказати дає людям неправильну думку. Це не втече. ]]]]><![CDATA[>не є якась магічна послідовність для ]]>. ]]]]>містить ]]символи як дані та ]]>закінчує поточний розділ CDATA. <![CDATA[>запускає новий розділ CDATA і ставить >у нього. Вони насправді є двома різними елементами і будуть розглядатися по-різному при роботі з аналізатором DOM. Ви повинні знати про це. Цей спосіб зробити його подібний ]]]><![CDATA[]>, за винятком того, що він ставиться ]в першому та ]>в другому CDATA. Різниця залишається.
Айдіакапі

Різниця завищена, оскільки вміст CDATA трактується як буквальний проміжок тексту, що вийшов. Це дійсно має значення лише тоді, коли возиться з DOM, і на цьому рівні ти все одно маєш справу з іншими невидимими межами, такими як вузли тексту, коментарів та обробки інструкцій.
Beejor

7

Відповідь С. Лотта правильна: ви не кодуєте кінцевий тег, ви розбиваєте його на кілька розділів CDATA.

Як вирішити цю проблему в реальному світі: використовуючи редактор XML, щоб створити XML-документ, який подаватиметься в систему управління вмістом, спробуйте написати статтю про розділи CDATA. Ваш звичайний трюк вбудовування зразків коду в розділ CDATA не зможе вас тут. Ви можете уявити, як я це дізнався.

Але за більшості обставин ви не зіткнетеся з цим, і ось чому: якщо ви хочете зберегти (сказати) текст документа XML як вміст елемента XML, ви, ймовірно, будете використовувати метод DOM, наприклад:

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

І DOM цілком обґрунтовано уникає <і>>, це означає, що ви ненавмисно не вклали розділ CDATA у свій документ.

О, і це цікаво:

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

Це, мабуть, ідеосинкразія .NET DOM, але це не є винятком. Тут викидається виняток:

Console.Write(doc.OuterXml);

Я здогадуюсь, що те, що відбувається під кришкою, полягає в тому, що XmlDocument використовує XmlWriter для отримання свого результату, і XmlWriter перевіряє наскільки добре сформований, як пише.


Ну, я мав майже «реальний світ» приклад. Зазвичай я завантажую Xml з Flash, який містить розмітку html у розділах CDATA. Думаю, спосіб уникнути цього може бути корисним. Але в будь-якому випадку, вміст CDATA зазвичай є дійсним XHTML, і тому "зовнішньої" CDATA можна було б уникнути взагалі.
Хуан Пабло Каліфано

2
CDATA майже завжди можна уникнути. Я вважаю, що люди, які борються з CDATA дуже часто, не розуміють, що вони насправді намагаються зробити та / або як насправді працює технологія, яку вони використовують.
Роберт Россні

О, я також повинен додати, що єдиною причиною того, що CMS, на який я наголошував у своїй відповіді, було використання CDATA, було те, що я написав це, і я не розумів, що я насправді намагався зробити та / або як працює технологія. Мені не потрібно було використовувати CDATA.
Роберт Россні

Якщо ви використовуєте .net, попередній коментар щодо уникнення CDATA є місцем - просто запишіть вміст у вигляді рядка, і рамка зробить все, що вийде (і не змінить масштаб на прочитане) для вас з реального світу .... ... xmlStream.WriteStartElement ("UnprocessedHtml"); xmlStream.WriteString (UnprocessedHtml); xmlStream.WriteEndElement ();
Марк Маллін


3

Ось ще один випадок, коли ]]>потрібно уникати. Припустимо, нам потрібно зберегти ідеально правильний HTML-документ у блоці CDATA документа XML, а у джерела HTML є власний блок CDATA. Наприклад:

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

коментований суфікс CDATA потрібно змінити на:

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

оскільки XML-аналізатор не знає, як обробляти блоки коментування JavaScript


Це не окремий випадок. Просто замініть ]]>на ]]]]><![CDATA[>все ще застосовується тут. Те, що це JavaScript або коментується, не важливо.
Томас Грейнджер


1

Чистіший спосіб у PHP:

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

Не забудьте використовувати мультибайтовий безпечний str_replace, якщо потрібно (не латинська1 $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;
   }

Чи можете ви пояснити свій голос? Сказати, що я помилився, не так корисно, як пояснити, де це.
Ален Тімбло

Немає необхідності робити багатобайтову безпечну заміну, якщо ви використовуєте UTF-8. Я не брав участь в голосі :)
frodeborli

-1

Я не думаю, що переривання CDATA - це хороший шлях. Ось моя альтернатива ...

Використовуйте ]для послідовності втечі з наступним шістнадцятковим значенням вашого персонажа. Як і в &#xhhhh;=>]<unicode value>;

Таким чином, якщо ви спробуєте записати, ]]>ваш кодування fn видасть, ]005D;]005D;]003E;що в CDATA нормально.

Це краще, ніж бігти за назвою сутності, тому що вони не розшифровуються щоразу у вашій програмі, і у вас можуть бути різні пріоритети для втечі сутностей з амперсом та уникненням інших символів / послідовностей. Як результат, ви маєте більше контролю над вмістом CDATA.


-2

Дивіться цю структуру:

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

Для внутрішніх тегів CDATA ви повинні закрити, ]]]]><![CDATA[>а не ]]>. Просто як це.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.