Есть ли способ избежать маркера конца CDATA в XML? - PullRequest
123 голосов
/ 22 октября 2008

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

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

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

Edit:

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

Ответы [ 10 ]

166 голосов
/ 22 октября 2008

Вы должны разбить ваши данные на части, чтобы скрыть ]]>.

Вот и все:

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

Первый <![CDATA[]]]]> имеет ]]. Второй <![CDATA[>]]> имеет >.

133 голосов
/ 22 октября 2008

Понятно, этот вопрос чисто академический. К счастью, у него есть очень определенный ответ.

Вы не можете избежать конечной последовательности 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>]]> 
15 голосов
/ 31 марта 2011

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

Кстати,

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

7 голосов
/ 22 октября 2008

S. Ответ Лотта правильный: вы не кодируете конечный тег, вы разбиваете его на несколько разделов 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 проверяет правильность формы при записи.

5 голосов
/ 31 марта 2016

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

3 голосов
/ 08 июня 2012

Вот еще один случай, когда ]]> необходимо экранировать. Предположим, нам нужно сохранить совершенно корректный HTML-документ в блоке CDATA XML-документа, а у источника HTML есть собственный блок CDATA. Например:

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

прокомментированный суффикс CDATA необходимо изменить на:

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

, поскольку синтаксический анализатор XML не будет знать, как обрабатывать блоки комментариев JavaScript

1 голос
/ 23 августа 2013

Более чистый путь в 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;
   }
1 голос
/ 21 марта 2013

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

0 голосов
/ 23 ноября 2017

Показать эту структуру:

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

Для внутренних тегов CDATA вы должны закрыться с ]]]]><![CDATA[> вместо ]]>. Все просто.

0 голосов
/ 03 августа 2016

Другим решением является замена ]]> на ]]]><![CDATA[]>.

...