有没有办法在 xml 中转义 CDATA 结束令牌?
Posted
技术标签:
【中文标题】有没有办法在 xml 中转义 CDATA 结束令牌?【英文标题】:Is there a way to escape a CDATA end token in xml? 【发布时间】:2010-09-18 10:17:13 【问题描述】:我想知道是否有任何方法可以在 xml 文档的 CDATA 部分中转义 CDATA 结束令牌 (]]>
)。或者,更一般地说,如果在 CDATA 中使用了一些转义序列(但如果它存在,我想它可能只对转义开始或结束标记有意义,无论如何)。
基本上,您能否在 CDATA 中嵌入开始或结束标记,并告诉解析器不要对其进行解释,而是将其视为另一个字符序列。
也许,如果您发现自己尝试这样做,您应该重构您的 xml 结构或代码,但即使过去 3 年左右我每天都在使用 xml,而且我从来没有这个问题,我想知道是否可能。只是出于好奇。
编辑:
除了使用 html 编码...
【问题讨论】:
首先,我接受正确的答案,但请注意:没有什么可以阻止某人在 CData 中将>
编码为 >
,以确保嵌入的 ]]>
不会被解析为 CDEnd。它只是意味着它是意外的,&
也必须首先编码为&
,以便可以正确解码数据。文档的用户也必须知道解码这个 CData。这并非闻所未闻,因为 CData 的部分目的是包含特定消费者了解如何处理的内容。这样的 CData 不能被任何通用消费者正确解释。
@nix,CDATA 只是提供了一种明确的方式来声明文本节点内容,这样(除了 ]]>)中的语言标记不会被解析。它特别不扩展像 > 这样的实体引用。因此,在 CDATA 块中,这仅表示这四个字符,而不是“>”。换个角度来看:在 xml 规范中,所有文本内容都称为“cdata”,而不仅仅是这些序列(“字符数据”)。这也与特定的消费代理无关。 (这样的事情确实存在——处理指令()。
(我应该补充一点,即使这种事情与节点的初衷背道而驰,但在与 XML 的漫长而痛苦的战斗中一切都是公平的。我只是觉得它可能对读者有用要知道 实际上并不是为此目的而设计的。)
@Semicolon CDATA
旨在允许任何东西:它们用于转义包含字符的文本块,否则这些字符会被识别为标记这也意味着CDATA
,因为它也是标记。但是,事实上,你不需要我暗示的双重编码。 ]]>
是在CDATA
中编码CDEnd
的可接受方式。
没错,你不需要双重编码——但你仍然需要代理具有特殊知识,因为解析器不会解析 >作为 >。这就是你的意思,我想?解析后可以根据需要替换它们吗?
【参考方案1】:
你必须将你的数据分成几部分来隐藏]]>
。
这就是全部内容:
<![CDATA[]]]]><![CDATA[>]]>
第一个<![CDATA[]]]]>
具有]]
。第二个<![CDATA[>]]>
有>
。
【讨论】:
感谢您的回答。我宁愿寻找类似反斜杠的东西(在 C、php、Java 等的字符串中)。按照ddaa引用的规则,好像没有这种事。 这应该是公认的答案。 Escaping 是一个有点含糊的术语,但这个答案肯定体现了 escaping 的精神。太糟糕了,它不符合 OP 的 转义 的狭隘概念,由于某种原因,它任意要求反斜杠字符参与其中。 总而言之,将]]>
转义为]]]]><![CDATA[>
。 5倍的长度……哇。但是,这是一个不常见的序列。
5x 的长度不仅有趣,而且在代码中也不是不常见的序列,这是 CDATA 的主要用例!假设压缩的 javascript 删除了空格,您可以通过索引从名称数组中按名称访问字段,例如“if(fields[fieldnames[0]]>3)”,现在您必须将其更改为“if( fields[fieldnames[0]]]]>3)",这违背了使用 CDATA 使其更具可读性的目的,哈哈。我想口头上给想出 CDATA 语法的人一巴掌。
那些争论“逃脱”含义的人是迂腐的。这就像说你不能调用 a='<scr'+'ipt>'
或 foo.com/bar%20gaz
转义,只是因为虽然语言上准确,但它不是确切的技术术语。是的,有多个 CDATA 部分,是的,在极少数情况下这很重要。但根据牛津的说法,计算中的广义定义是“导致后续字符被不同地解释”。在这种情况下和提到的情况下,都会发生这种情况。【参考方案2】:
显然,这个问题纯粹是学术性的。幸运的是,它有一个非常确定的答案。
您不能转义 CDATA 结束序列。 XMLspecification的生产规则20很清楚:
[20] CData ::= (Char* - (Char* ']]>' Char*))
编辑:此产品规则的字面意思是“CData 部分可能包含您想要的任何内容,但序列 ']]>'。没有例外。”。
EDIT2:same section 还写着:
在 CDATA 部分中,只有 CDEnd 字符串被识别为标记,因此左尖括号和 & 符号可能以其文字形式出现;它们不需要(也不能)使用“
&lt;
”和“&amp;
”进行转义。 CDATA 部分不能嵌套。
换句话说,不能使用实体引用、标记或任何其他形式的解释语法。 CDATA 部分中唯一解析的文本是]]>
,它终止了该部分。
因此,无法在 CDATA 部分中转义 ]]>
。
EDIT3:same section 还写着:
2.7 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>]]>
【讨论】:
确实如此。好吧,我不是学术类型,但正如我在问题中所说,我只是对此感到好奇。老实说,我只是相信你的话,因为我几乎无法理解用于规则的语法。谢谢你的回答。 这不是一个学术问题。考虑一篇博客文章的 RSS 提要,其中包含有关 CDATA 的讨论。 我的意思是“学术”的意思:“讨论很有趣,但没有实际用途”。一般情况下,CDATA 没什么用,它只是一种序列化 XML 文本的方式,在语义上相当于使用字符实体 < 转义特殊字符。 >和“。字符实体是最简单、最健壮和最通用的解决方案,因此请使用它来代替 CDATA 部分。如果您使用适当的 XML 库(而不是使用字符串构建 XML),您甚至不必考虑它。 我刚刚被这个咬了,因为我试图将一些压缩的 Javascript 编码到一个 我在现实世界中经历过。在阅读***转储并编写另一个 xml 文件时,我在National Transportation Safety Board 的页面上遇到了这个问题。它在信息框中包含 US$>1 亿(2013 年)的预算。源 xml 包含[[United States dollar|US$]]&gt;100 million (2013)
,读者将其翻译为 [[United States dollar|US$]]>100 million (2013)
,作者选择使用 CDATA 转义文本并失败。【参考方案3】:
您不会转义]]>
,而是通过在>
之前插入]]><![CDATA[
在]]
之后转义>
,就像C/Java/PHP/Perl 中的\
一样字符串,但只需要在 >
之前和 ]]
之后。
顺便说一句,
S.Lott 的回答与此相同,只是措辞不同。
【讨论】:
这种说法让人误解。这是不转义。]]]]><![CDATA[>
不是 ]]>
的神奇序列。 ]]]]>
将 ]]
字符作为数据,]]>
结束当前 CDATA 部分。 <![CDATA[>
开始一个新的 CDATA 部分并将 >
放入其中。它们实际上是两个不同的元素,在使用 DOM 解析器时会被区别对待。你应该意识到这一点。这种执行方式类似于]]]><![CDATA[]>
,不同之处在于它将]
放在第一个CDATA 中,]>
放在第二个CDATA 中。区别依然存在。
差异被夸大了,因为 CDATA 内容被视为转义文本的文字范围。只有在搞乱 DOM 时才真正重要,并且在那个级别上,您正在处理其他不可见的边界,例如文本、注释和处理指令节点。【参考方案4】:
只需将]]>
替换为]]]]><![CDATA[>
【讨论】:
谢谢。这应该是最佳答案。【参考方案5】: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 在写入时检查格式是否正确。
【讨论】:
嗯,我有一个几乎“真实世界”的例子。我通常从包含 CDATA 部分中的 html 标记的 Flash 加载 Xml。我想,有办法逃脱它可能会很有用。但无论如何,在这种情况下,CDATA 内容通常是有效的 XHTML,因此可以完全避免“外部”CDATA。 CDATA 几乎总是可以完全避免。我发现经常与 CDATA 斗争的人不了解他们真正想要做什么和/或他们使用的技术如何真正发挥作用。 哦,我还应该补充一点,我在回答中提到的 CMS 使用 CDATA 的唯一原因是我写了它,我不明白我真正想要做什么和/或者技术是如何工作的。我不需要使用 CDATA。 如果您使用的是 .net,前面关于 CDATA 可避免的评论是正确的 - 只需将内容写为字符串,框架将为您完成所有转义(以及读取时的转义)来自现实世界....... xmlStream.WriteStartElement("UnprocessedHtml"); xmlStream.WriteString(UnprocessedHtml); xmlStream.WriteEndElement();【参考方案6】:这是另一个需要转义]]>
的情况。假设我们需要在 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 或已注释的事实并不重要。【参考方案7】:
在 PHP 中:'<![CDATA['.implode(explode(']]>', $string), ']]]]><![CDATA[>').']]>'
【讨论】:
【参考方案8】:一种更简洁的 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,则无需进行多字节安全替换。不过我没有投反对票:)【参考方案9】:查看这个结构:
<![CDATA[
<![CDATA[
<div>Hello World</div>
]]]]><![CDATA[>
]]>
对于内部 CDATA 标记,您必须使用 ]]]]><![CDATA[>
而不是 ]]>
关闭。就这么简单。
【讨论】:
以上是关于有没有办法在 xml 中转义 CDATA 结束令牌?的主要内容,如果未能解决你的问题,请参考以下文章