138

我想知道是否有任何方法可以]]>在 xml 文档的 CDATA 部分中转义 CDATA 结束标记 ()。或者,更一般地说,如果在 CDATA 中使用了一些转义序列(但如果它存在,我想它可能只对转义开始或结束标记有意义,无论如何)。

基本上,您是否可以在 CDATA 中嵌入开始或结束标记,并告诉解析器不要解释它,而是将其视为另一个字符序列。

或许,如果您发现自己尝试这样做,您应该重构您的 xml 结构或代码,但即使我在过去 3 年左右每天都在使用 xml,而且我从来没有遇到过这个问题,我想知道这是否可能。只是出于好奇。

编辑:

除了使用 html 编码...

4

9 回答 9

175

您必须将数据分解成碎片以隐藏]]>.

这是整个事情:

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

第一个<![CDATA[]]]]>]]. 第二个<![CDATA[>]]>>

于 2008-10-21T22:27:56.540 回答
148

显然,这个问题纯粹是学术性的。幸运的是,它有一个非常明确的答案。

您不能转义 CDATA 结束序列。XML规范的生产规则 20非常明确:

[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>]]> 
于 2008-10-21T22:31:04.210 回答
17

您不会逃避,]]>而是通过在 之前插入来逃避>之后,就像C/Java/PHP/Perl 字符串中的 a 一样,但只需要在 a 之前和之后 a 。]]]]><![CDATA[>\>]]

顺便提一句,

S.Lott 的回答与此相同,只是措辞不同。

于 2011-03-30T20:24:37.557 回答
15

只需替换]]>]]]]><![CDATA[>

于 2016-03-31T11:09:20.940 回答
7

S. Lott 的回答是正确的:您不对结束标签进行编码,而是将其拆分为多个 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 在写入时检查格式是否正确。

于 2008-10-22T00:00:47.087 回答
3

这是另一种]]>需要转义的情况。假设我们需要在 XML 文档的 CDATA 块中保存一个完全有效的 HTML 文档,而 HTML 源恰好有它自己的 CDATA 块。例如:

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

注释的 CDATA 后缀需要更改为:

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

因为 XML 解析器不知道如何处理 javascript 注释块

于 2012-06-08T05:34:38.803 回答
1

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

于 2013-03-21T09:49:09.390 回答
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;
   }
于 2013-08-23T14:57:40.467 回答
-2

看这个结构:

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

对于内部 CDATA 标记,您必须使用]]]]><![CDATA[>而不是]]>. 就那么简单。

于 2017-11-22T23:33:34.640 回答