Есть ли способ избежать конечного токена CDATA в xml?
Вопрос
Мне было интересно, есть ли какой-нибудь способ избежать конечного токена CDATA (]]>
) в разделе CDATA в xml-документе.Или, в более общем плане, если есть какая-то escape-последовательность для использования внутри CDATA (но если она существует, я думаю, в любом случае, вероятно, имело бы смысл экранировать токены begin или end только).
В принципе, можете ли вы встроить токен begin или end в CDATA и указать анализатору не интерпретировать его, а рассматривать как просто еще одну последовательность символов.
Вероятно, вам следует просто реорганизовать свою структуру xml или свой код, если вы обнаружите, что пытаетесь это сделать, но, хотя я работаю с xml ежедневно в течение последних 3 лет или около того и у меня никогда не возникало этой проблемы, мне было интересно, возможно ли это.Просто из любопытства.
Редактировать:
Кроме использования html-кодировки...
Решение
Очевидно, что этот вопрос носит чисто академический характер.К счастью, на это есть вполне определенный ответ.
Вы не можете экранировать конечную последовательность CDATA.Производственное правило 20 XML спецификация совершенно ясно:
[20] CData ::= (Char* - (Char* ']]>' Char*))
Редактировать:Это правило продукта буквально означает "Раздел CData может содержать все, что вы хотите, КРОМЕ последовательности ']]>'.Никаких исключений"..
РЕДАКТИРОВАТЬ 2:В тот же раздел также читает:
В разделе CDATA только строка CDEnd распознается как разметка, так что левые угловые скобки и амперсанды могут встречаться в их буквальной форме;их не нужно (и не может) экранировать с помощью "
<
" и "&
".Разделы CDATA не могут быть вложены.
Другими словами, невозможно использовать ссылку на сущность, разметку или любую другую форму интерпретируемого синтаксиса.Единственным анализируемым текстом внутри раздела CDATA является ]]>
, и это завершает раздел.
Следовательно, сбежать невозможно ]]>
внутри раздела CDATA.
РЕДАКТИРОВАТЬ 3:В тот же раздел также читает:
2.7 Разделы CDATA
[Определение:Разделы CDATA могут встречаться в любом месте, где могут встречаться символьные данные;они используются для экранирования блоков текста, содержащих символы, которые в противном случае были бы распознаны как разметка.Разделы CDATA начинаются со строки "<![CDATA[" and end with the string "]]>":]
Тогда в любом месте, где могут встречаться символьные данные, может быть раздел CDATA, включая множество смежных разделов CDATA вместо одного раздела CDATA.Это позволяет разделить ]]>
токен и поместите две его части в смежные разделы CDATA.
бывший:
<![CDATA[Certain tokens like ]]> can be difficult and <invalid>]]>
должно быть записано как
<![CDATA[Certain tokens like ]]]]><![CDATA[> can be difficult and <valid>]]>
Другие советы
Вы должны разбить свои данные на части, чтобы скрыть ]]>
.
Вот в чем дело в целом:
<![CDATA[]]]]><![CDATA[>]]>
Первый <![CDATA[]]]]>
имеет ]]
.Второй <![CDATA[>]]>
имеет >
.
Вы не избегаете ]]>
но вы избегаете >
после ]]
вставив ]]><![CDATA[
перед тем, как >
, думайте об этом просто как о \
в C / Java / PHP / Perl строка, но необходимая только перед >
и после того , как ]]
.
КСТАТИ,
Ответ С. Лотта такой же, как этот, просто сформулирован по-другому.
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 проверяет правильность оформления при записи.
просто замените ]]>
с ]]]]><![CDATA[>
Вот еще один случай, в котором ]]>
нужно сбежать.Предположим, нам нужно сохранить абсолютно корректный HTML-документ внутри блока CDATA XML-документа, и так получилось, что у источника HTML есть собственный блок CDATA.Например:
<htmlSource><![CDATA[
... html ...
<script type="text/javascript">
/* <![CDATA[ */
-- some working javascript --
/* ]]> */
</script>
... html ...
]]></htmlSource>
прокомментированный суффикс CDATA необходимо изменить на:
/* ]]]]><![CDATA[> *//
поскольку анализатор XML не будет знать, как обрабатывать блоки комментариев javascript
В PHP: '<![CDATA['.implode(explode(']]>', $string), ']]]]><![CDATA[>').']]>'
Более чистый способ в 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;
}
Другое решение заключается в замене ]]>
Автор: ]]]><![CDATA[]>
.
Посмотрите на эту структуру:
<![CDATA[
<![CDATA[
<div>Hello World</div>
]]]]><![CDATA[>
]]>
Для внутренних тегов CDATA вы должны закрыть с помощью ]]]]><![CDATA[>
вместо того , чтобы ]]>
.Вот так просто.