Есть ли способ избежать конечного токена CDATA в xml?

130

Мне было интересно, есть ли способ избежать конечного токена CDATA ( ]]>) в разделе CDATA в документе xml. Или, в более общем смысле, если есть какая-то escape-последовательность для использования в CDATA (но если она существует, я думаю, что, в любом случае, имеет смысл только избегать токенов начала или конца).

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

Возможно, вам следует просто провести рефакторинг своей xml-структуры или кода, если вы обнаружите, что пытаетесь это сделать, но хотя я работал с xml ежедневно в течение последних 3 лет или около того, и у меня никогда не было этой проблемы, Мне было интересно, возможно ли это. Просто из любопытства.

Редактировать:

Кроме использования кодировки html ...

Хуан Пабло Калифано
источник
4
Во-первых, я принимаю ответ как правильный, но обратите внимание: ничто не мешает кому-либо кодировать, >как >в CData, чтобы гарантировать, что встроенное ]]>не будет проанализировано как CDEnd. Это просто означает, что это неожиданно, и это &должно быть ПЕРВОЕ закодировано, &чтобы данные можно было правильно декодировать. Пользователи документа должны знать, как декодировать эти CData. Это не является чем-то необычным, поскольку часть цели CData состоит в том, чтобы содержать контент, с которым конкретный потребитель понимает, как обращаться. Нельзя ожидать, что такая CData будет правильно интерпретирована любым универсальным потребителем.
nix
1
@nix, CDATA просто предоставляет явный способ объявления содержимого текстового узла, так что языковые токены внутри (кроме]]> не анализируются. Он специально не расширяет ссылки на объекты, такие как & gt; по этой причине в блоке CDATA это означает только эти четыре символа, а не '>'. Для сравнения: в спецификации xml все текстовое содержимое называется «cdata», а не только эти последовательности («символьные данные»). И дело не в конкретных потребителях. (Такая вещь действительно существует - инструкции по обработке (<? Target инструкция?>).
Точка с запятой,
(Я должен добавить, даже если такие вещи противоречат первоначальному замыслу узла, все будет честно в долгой и мучительной битве с XML. Я просто считаю, что читателям может быть полезно знать, что <! [CDATA [ ]]> на самом деле не был предназначен для этой цели.)
Точка с запятой
1
@Semicolon CDATAбыл разработан, чтобы разрешить все : они используются для экранирования блоков текста, содержащих символы, которые в противном случае были бы распознаны как разметка. Это также подразумевает, CDATAпоскольку это также разметка. Но на самом деле вам не нужно двойное кодирование, о котором я говорил. ]]&gt;является приемлемым средством кодирования CDEndв CDATA.
nix
Да, вам не понадобится двойное кодирование, но вам все равно понадобится агент, обладающий специальными знаниями, поскольку синтаксический анализатор не будет анализировать & gt; как>. Думаю, вы это имеете в виду? Что вы можете заменить их, как считаете нужным, после разбора?
Точка с запятой,

Ответы:

141

Ясно, что это чисто академический вопрос. К счастью, на него есть однозначный ответ.

Вы не можете избежать конечной последовательности CDATA. Правило производства 20 спецификации XML довольно ясно:

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

РЕДАКТИРОВАТЬ: Это правило продукта буквально означает «Раздел 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>]]> 
ddaa
источник
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
Я испытал это в реальном мире. Читая дамп википедии и записывая еще один 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
Таким образом, escape ]]>as ]]]]><![CDATA[>. В 5 раз длиннее ... вау. Но тогда это необычная последовательность.
Brilliand
5
Мало того, что длина 5x забавна, это даже не редкость в коде, который является основным вариантом использования CDATA! Предполагая сжатый JavaScript, который удаляет пробелы, вы можете получить доступ к полю по имени из массива имен по индексу, например, «if (fields [fieldnames [0]]> 3)», и теперь вам нужно изменить его на «if ( fields [fieldnames [0]]]]> <! [CDATA [> 3) », что противоречит цели использования CDATA, чтобы сделать его более читаемым, LOL. Я хотел бы словесно дать пощечину тому, кто придумал синтаксис CDATA.
Трийнко
1
Экранирование, или, точнее, цитирование, означает вставку некоторого текста в контекст, где необработанный текст имеет значение БЕЗ выхода из контекста. Это не имеет ничего общего с обратной косой чертой. И этот ответ не является экранированием или цитированием, поскольку он создает два раздела CDATA вместо одного.
ddaa
17

Вы не избегаете, ]]>но вы избегаете >после ]], вставляя ]]><![CDATA[перед >, думайте об этом так же, как \в строке C / Java / PHP / Perl, но необходимо только до >и после ]].

КСТАТИ,

Ответ С.Лотта такой же, только в другой формулировке.

Джейсон Пайерон
источник
2
Я предпочитаю эту формулировку. :)
Brilliand
3
Таким образом, люди получают неверное представление. Это никуда не деться. ]]]]><![CDATA[>это не какая-то магическая последовательность для ]]>. ]]]]>содержит ]]символы в качестве данных и ]]>завершает текущий раздел CDATA. <![CDATA[>запускает новый раздел CDATA и помещает >в него. На самом деле это два разных элемента, и при работе с парсером DOM они будут обрабатываться по-разному. Вы должны знать об этом. Этот способ выполнения аналогичен ]]]><![CDATA[]>, за исключением того, что он вводит ]первый и ]>второй CDATA. Разница остается.
Aidiakapi
Разница преувеличена, поскольку содержимое CDATA рассматривается как буквальный диапазон экранированного текста. Только когда возитесь с DOM, это действительно имеет значение, и на этом уровне вы все равно имеете дело с другими невидимыми границами, такими как текст, комментарии и узлы инструкций по обработке.
Beejor 03
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 ();
Марк Маллин
6

просто замените ]]>на]]]]><![CDATA[>

Томас Грейнджер
источник
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 или прокомментированный, не имеет значения.
Thomas Grainger
1

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

user2194495
источник
1

Более чистый способ в PHP:

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

Не забудьте при необходимости использовать многобайтовый str_replace (не 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;
   }
Ален Тьембло
источник
Вы можете объяснить свой отрицательный голос? Сказать, что я сделал ошибку, не так полезно, как объяснить, где она находится.
Ален Тьембло
При использовании UTF-8 нет необходимости выполнять безопасную многобайтовую замену. Я не голосовал против :)
frodeborli
-1

Я не думаю, что прерывание CDATA - хороший выход. Вот моя альтернатива ...

Используйте ]для escape-последовательности, за которой следует шестнадцатеричное значение вашего символа. Как в &#xhhhh;=>]<unicode value>;

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

Это лучше, чем экранирование по имени сущности, потому что они не декодируются каждый раз в вашем приложении, и у вас могут быть разные приоритеты для экранирования сущностей с амперсандом по сравнению с экранированием некоторых других символов / последовательностей. В результате у вас больше контроля над содержимым CDATA.

honzar
источник
-2

См. Эту структуру:

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

Для внутреннего тега (ов) CDATA вы должны закрыть ]]]]><![CDATA[>вместо ]]>. Просто как тот.

Чад Куэн
источник