有没有办法在xml中转义CDATA结束令牌?


129

我想知道是否有任何方法可以]]>在xml文档的CDATA节中转义CDATA结束标记()。或者,更一般而言,如果在CDATA中有一些转义序列可供使用(但是,如果存在,我想无论如何,转义开始或结束令牌可能才有意义)。

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

可能的话,如果您发现要尝试做的话,应该重构您的xml结构或代码,但是即使在过去三年左右的时间里我每天都在使用xml,但我从未遇到过这个问题,我想知道是否有可能。只是出于好奇。

编辑:

除了使用html编码...


4
首先,我接受正确的答案,但请注意:没有什么可以阻止某人将其编码>>CData,以确保]]>不会将嵌入式解析为CDEnd。它只是意味着它是意外的,因此也&必须首先进行编码,&以便可以正确解码数据。文档的用户也必须知道如何解码此CData。这并不是闻所未闻的,因为CData的部分目的是包含特定用户理解如何处理的内容。不能期望任何通用使用者都能正确解释这样的CData。
nix

1
@ nix,CDATA只是提供了一种声明文本节点内容的显式方式,这样就不会解析(除]]>内的语言标记)。具体来说,它不会扩展&gt;等实体引用。因此,在CDATA块中,仅表示这四个字符,而不是'>'。透视一下:在xml规范中,所有文本内容都称为“ cdata”,而不仅仅是这些序列(“字符数据”)。此外,它也不关乎特定的消费代理商。(虽然确实存在某种东西-处理指令(<?目标指令?>)。)
分号

(我应该补充,即使这种事情违背了节点的初衷,在与XML长期而艰苦的斗争中,一切都是公平的。我只是觉得对读者来说<![CDATA [ ]]>并非实际上是为此目的而设计的。)
分号

1
@Semicolon CDATA旨在允许任何操作它们用于转义包含字符的文本块,否则这些字符将被视为标记,这也暗示着,CDATA因为它也是标记。但是,实际上,您不需要我暗示的双重编码。]]&gt;CDEnd在内对a 进行编码的可接受方法CDATA
nix 2015年

是的,您不需要双重编码-但是您仍然需要代理具有特殊知识,因为解析器不会解析&gt; 如>。我想那是你的意思?可以在解析后替换为您认为合适的文件吗?
分号2015年

Answers:


141

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

您无法转义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>]]> 

1
确实。好吧,我不是一个学术型的人,但是正如我在问题中所说的那样,我对此感到很好奇。坦白地说,我会随便说一下,因为我几乎无法理解规则所使用的语法。感谢您的回答。
Juan Pablo Califano,

39
这不是一个学术问题。考虑博客文章的RSS提要,其中包含有关CDATA的讨论。
usr

4
我的意思是“学术的”:“有趣的讨论,但没有实际用途”。通常,CDATA没什么用,它只是序列化XML文本的一种方法,在语义上等效于使用字符实体&lt;来转义特殊字符。&gt; 和”。字符实体是最简单,最可靠和最通用的解决方案,因此请使用它代替CDATA部分。如果您使用适当的XML库(而不是从字符串中构建XML),您甚至不必考虑它。
ddaa 2012年

5
我只是被这个咬了,因为我试图将一些压缩的Javascript编码为<script>标签,例如:<script>/*<![CDATA[*/javascript goes here/*]]>*/</script>我的javascript只包含该序列!我喜欢将其分为多个CDATA部分的想法……
NickZoic 2012年

3
我在现实世界中经历过。在阅读Wikipedia转储并编写另一个xml文件时,我在国家运输安全委员会的页面上遇到了这个问题。它在信息框中包含的预算超过1亿美元(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引用的规则,似乎没有这种事情。
Juan Pablo Califano,

28
这应该是公认的答案。转义是一个略带歧义的术语,但是这个答案肯定可以解决转义的精神。太糟糕了,它不符合OP狭义的转义概念,出于某种原因,该概念任意需要反斜杠字符。
G-Wiz

5
因此,总而言之,请逃避]]>]]]]><![CDATA[>。长度的5倍...哇。但是,这是一个不常见的序列。
Brilliand

5
5倍长度不仅很有趣,而且在代码中也不是不常见的序列,这是CDATA的主要用例!假设压缩的JavaScript删除了空格,那么您可能正在从按索引命名的数组中按名称访问字段,例如“ if(fields [fieldnames [0]]> 3)”,现在您必须将其更改为“ if( fields [fieldnames [0]]]]> <![CDATA [> 3)”,这违反了使用CDATA使其更具可读性的目的,哈哈。我想口头上拍那些想出CDATA语法的人。
Triynko 2013年

1
转义(或更正确地引用)是指在上下文中插入一些文本,而原始文本没有离开上下文的含义。它与反斜杠无关。而且此答案不是转义或引用,因为它产生两个CDATA部分而不是一个。
ddaa

17

你不逃避]]>,但是你逃脱>后,]]通过插入]]><![CDATA[之前>,想到这就像一个\在C /的Java / PHP / Perl的字符串,但之前只需要>和经过]]

顺便说一句,

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


2
我喜欢这种措辞。:)
Brilliand

3
这种说法给人以错误的想法。这不是逃避。]]]]><![CDATA[>这不是神奇的顺序]]>]]]]>]]字符作为数据,并]]>结束当前的CDATA节。<![CDATA[>启动一个新的CDATA节并将其放入>。它们实际上是两个不同的元素,在使用DOM解析器时将被区别对待。您应该意识到这一点。这样做的方法与相似]]]><![CDATA[]>,不同之处在于,它放置]在第一个和]>第二个CDATA中。区别仍然存在。
Aidiakapi 2013年

差异被夸大了,因为CDATA内容被视为转义文本的文字范围。只有当与DOM混淆时,它才真正重要,并且在该级别上,您无论如何都在处理其他不可见的边界,例如文本,注释和处理指令节点。
Beejor

7

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


好吧,我有一个几乎“真实世界”的例子。我通常从Flash加载Xml,该Flash在CDATA部分中包含html标记。我想,有一种方法可以摆脱它。但是无论如何,在那种情况下,CDATA内容通常是有效的XHTML,因此可以完全避免使用“外部” CDATA。
Juan Pablo Califano,

2
几乎可以完全避免使用CDATA。我发现那些经常与CDATA斗争的人不了解他们真正在试图做什么和/或他们使用的技术是如何真正起作用的。
罗伯特·罗斯尼

哦,我还要补充一点,我在答案中提到的CMS使用CDATA的唯一原因是我写了它,而我却不明白我实际上在试图做什么和/或该技术如何工作。我不需要使用CDATA。
罗伯特·罗斯尼

如果您使用的是.net,那么前面关于CDATA可避免的注释就很明显-只需将内容编写为字符串即可,框架将为您完成所有现实世界中的转义(并在读取时进行转义)...。 ... xmlStream.WriteStartElement(“ UnprocessedHtml”); xmlStream.WriteString(UnprocessedHtml); xmlStream.WriteEndElement();
马克·穆林


3

这是另一种]]>需要逃脱的情况。假设我们需要在XML文档的CDATA块中保存一个完全有效的HTML文档,并且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[>').']]>'


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

你能解释一下你的反对票吗?说我犯了一个错误并没有解释它在哪里的作用。
Alain Tiemblo 2014年

如果您使用的是UTF-8,则无需进行多字节安全替换。我没有
投票

-1

我不认为中断CDATA是个好方法。这是我的选择...

使用]的转义序列,然后你的角色的十六进制值。就像在&#xhhhh;=>]<unicode value>;

这样,如果您尝试记录]]>您的编码fn,则会]005D;]005D;]003E;在CDATA中产生正常结果。

最好不要按实体名称进行转义,因为在您的应用程序中不会每次都对它们进行解码,并且使用“&”转义实体与转义其他字符/序列可能会具有不同的优先级。因此,您可以更好地控制CDATA的内容。


-2

看到以下结构:

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

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.