XML 解析器错误:未定义实体

Posted

技术标签:

【中文标题】XML 解析器错误:未定义实体【英文标题】:XML parser error: entity not defined 【发布时间】:2011-04-17 19:56:03 【问题描述】:

我在 *** 上搜索过这个问题,确实找到了一些主题,但我觉得在这个问题上对我来说并没有一个可靠的答案。

我有一个用户提交的表单,该字段的值存储在一个 XML 文件中。 XML 设置为使用 UTF-8 编码。

用户不时会从某处复制/粘贴文本,这时我会收到“实体未定义错误”。

我意识到 XML 只支持少数几个实体,除此之外的任何内容都无法识别 - 因此出现解析器错误。

据我所知,我看到了几个选项:

    我可以找到并替换所有   并将它们替换为   或实际空间。 我可以将有问题的代码放在 CDATA 部分中。 我可以在 XML 文件中包含这些实体。

我对 XML 文件所做的是,用户可以将内容输入到表单中,将其存储在 XML 文件中,然后该内容在网页上显示为 Xhtml(使用 SimpleXML 解析)。

在这三个选项中,或者我不知道的任何其他选项中,处理这些实体的最佳方式是什么?

谢谢, 瑞恩

更新

我要感谢大家的精彩反馈。我实际上确定了导致我的实体错误的原因。所有的建议让我更深入地研究它!

一些文本框是普通的旧文本框,但我的文本区域使用 TinyMCE 进行了增强。事实证明,在仔细观察时,php 警告总是引用来自 TinyMCE 增强文本区域的数据。后来我注意到在 PC 上所有字符都被取出(因为它无法读取它们),但在 MAC 上你可以看到引用该字符的 unicode 编号的小方框。它首先出现在 MAC 上的原因是因为我使用 utf8_encode 对非 UTF 格式的数据进行编码以防止其他解析错误(这在某种程度上也与 TinyMCE 有关)。

解决这一切的方法很简单:

我在 tinyMCE.init 中添加了这一行 entity_encoding : "utf-8"。现在,所有角色都以他们应该的方式出现了。

我想我唯一不明白的是为什么字符放在文本框中时仍然显示,因为没有任何东西可以将它们转换为 UTF,但是对于 TinyMCE,这是一个问题。

【问题讨论】:

您问题的某些重要部分是不可见的,因为它们被解析为标记。请用反引号 (``) 将这些位括起来。 @LarsH:嗯,我在问题源中没有看到任何需要这个的东西。 @Tomalak:“1. 我可以找到并替换所有 ?? 并将它们替换为 ?? 或实际空间。”当然在我看来好像少了点什么。 @LarsH:哦,你是对的。我没有注意到这些。只有几个代表可以为您服务,您可以自己编辑问题。 :) 【参考方案1】:

我同意这纯粹是一个编码问题。在 PHP 中,这就是我解决这个问题的方法:

    在将 html 片段传递给 SimpleXMLElement 构造函数之前,我使用 html_entity_decode 对其进行了解码。

    然后使用utf8_encode()对其进行进一步编码。

$headerDoc = '<temp>' . utf8_encode(html_entity_decode($headerFragment)) . '</temp>'; 
$xmlHeader = new SimpleXMLElement($headerDoc);

现在上面的代码不会抛出任何未定义实体错误。

【讨论】:

如果您将"UTF-8"html_entity_decode 作为第三个参数,例如,您可能可以在不使用utf8_encode 的情况下逃脱。 html_entity_decode($headerFragment, null, "UTF-8")【参考方案2】:

您可以对文本进行 HTML 解析,并仅使用相应的数字实体将其重新转义(例如:&amp;nbsp;&amp;#160;)。在任何情况下——简单地使用未经处理的用户输入是一个坏主意。

所有数字实体都可以在 XML 中使用,只有从 HTML 中已知的命名实体不起作用(&amp;amp;&amp;quot;&amp;lt;&amp;gt;&amp;apos; 除外)。

但大多数时候,您只需将实际字符 (&amp;ouml;ö) 写入 XML 文件,因此根本不需要使用实体引用。如果您使用 DOM API 来操作您的 XML(您应该这样做!),这是您最安全的选择。

最后(这是懒惰的开发人员解决方案)您可以构建一个损坏的 XML 文件(即格式不正确,存在实体错误),只需 pass it through tidy 进行必要的修复。这可能会起作用,也可能会失败,这取决于 整个事情的破坏程度。不过,根据我的经验,tidy 非常聪明,可以让你摆脱很多事情。

【讨论】:

“您可以对文本进行 HTML 解析并使用相应的数字实体对其进行重新转义”——这是否意味着您始终可以将数字实体存储在 HTML 文本实体之上? -瑞安 @Ryan:是的,数字实体在 XML 和 HTML 中都允许(并且可以被识别)。 @Tomalak 这意味着我必须事先知道所有实体的名称及其数字实体,对吗?如果我把它们都加进去,那会不会是非常密集的处理? -瑞安 @Ryan:有些函数可以知道所有实体名称,您不必手动执行此操作。这就是我所说的“HTML 解析”。使用 HTML 解析器进行此类工作。 @Tomalak 在您的一段中,您建议您可以存储实际字符,所以从技术上讲,在将其写入 XML 文件之前,我可以只使用 html_entity_decode 来获取字符吗? -瑞安【参考方案3】:

1。我可以找到并替换所有 [&amp;nbsp;?] 并将它们替换为 [&amp;#160;?] 或实际空间。

这是一种稳健的方法,但它要求您拥有一个包含所有 HTML 实体的表格(我假设粘贴的输入来自 HTML)并解析粘贴的文本以获取实体引用。

2。我可以将有问题的代码放在 CDATA 部分中。

换句话说,禁用整个部分的解析?然后你将不得不以其他方式解析它。可以工作。

3。我可以在 XML 文件中包含这些实体。

您的意思是包括实体定义?如果您不介意使 XML 文件更大一些,我认为这是一种简单而可靠的方法。您可以有一个“包含”文件(在网上找到一个),它是一个外部实体,您可以从主 XML 文件的顶部引用它。

一个缺点是您使用的 XML 解析器必须是一个处理外部实体的解析器(并非所有解析器都需要这样做)。它必须正确地将外部实体的(可能是相对的)URL 解析为可访问的东西。这还不错,但可能会增加对您的处理工具的限制。

4。您可以在粘贴的内容中禁止非 XML。除其他外,这将禁止未在 XML 中预定义的实体引用(Tomalak 提到的 5 个)或在内容本身中定义的实体引用。但是,如果用户需要能够在其中粘贴 HTML,这可能会违反应用程序的要求。

5。您可以通过设置 someDiv.innerHTML = thePastedContent; 将粘贴的内容作为 HTML 解析到 DOM 树中。 换句话说,在某处创建一个 div(可能 display=none,调试除外)。假设您有一个 javascript 变量 myDiv 保存此 div 元素,另一个变量 myField 保存您的输入文本字段元素。然后在javascript中你做

myDiv.innerHTML = myField.value;

它从 myField 中获取未解析的文本,将其解析为 HTML DOM 树,并将其作为 HTML 内容粘贴到 myDiv 中。

然后您将使用一些基于浏览器的方法将 DOM 树序列化(=“反解析”)回 XML。参见例如this question。然后将结果作为 XML 发送到服务器。

您是想在浏览器还是服务器上进行此修复(如@Hannes 建议的那样)将取决于数据的大小、响应的速度、服务器的功能以及您是否关心关于黑客故意发送格式不正确的 XML。

【讨论】:

@Tomalak - 为什么会 ö成为&ouml;?当文本放入innerhtml时,它不会作为单个字符o-umlaut被解析到dom中吗? 1.可能开销太大了,对吧? 2. 再想一想,这似乎适得其反,所以我将取消该选项。 3. 除了文件比较大,还有其他缺点吗?如果没有,我会说这就是要走的路。 4. 是的,那将违反要求。 5. 我不明白这个解决方案 - 你能提供更多细节吗? -瑞安 感谢您这样做! 3. 问题是:替换这些值或嵌入检查实体的 DTD 会花费更多的处理器时间字符串吗? 5. 好的,我现在明白了。我想在服务器上执行此操作。 -瑞安 @Ryan - 自己替换值可能更快,因为 DTD 处理更通用。但是您必须对其进行测试才能确定。【参考方案4】:

使用带有标志“ENT_XML1”的“htmlentities()”:htmlentities($value, ENT_XML1);

如果你使用“SimpleXMLElement”类:

$SimpleXMLElement-&gt;addChild($name, htmlentities($value, ENT_XML1));

【讨论】:

【参考方案5】:

如果您想转换所有字符,这可能会对您有所帮助(我之前写过):

http://www.lautr.com/convert-all-applicable-characters-to-numeric-entities-for-use-in-xml

function _convertAlphaEntitysToNumericEntitys($entity) 
  return '&#'.ord(html_entity_decode($entity[0])).';';


$content = preg_replace_callback(
  '/&([\w\d]+);/i',
  '_convertAlphaEntitysToNumericEntitys',
  $content);

function _convertAsciOver127toNumericEntitys($entity) 
  if(($asciCode = ord($entity[0])) > 127)
    return '&#'.$asciCode.';';
  else
    return $entity[0];


$content = preg_replace_callback(
  '/[^\w\d ]/i',
  '_convertAsciOver127toNumericEntitys', $content);

【讨论】:

好吧,如果你应用 "$content = preg_replace_callback('/&([\w\d]+);/i','_convertAlphaEntitysToNumericEntitys',$content);"所有 HTML 实体(  等等)都将更改为数字实体。之后应用“$content = preg_replace_callback('/[^\w\d ]/i','_convertAsciOver127toNumericEntitys'), $content);”并且每个超过 127 的字符(不是由 htmlspecialchars 处理的)都被转换为一个数字实体,如果我理解错了,你能举一个输入的例子 sn-p 吗? 对不起,我误解了你的代码做了什么。删除我之前的评论。【参考方案6】:

这个问题对于任何解析 XML 或 JSON 的语言(基本上是所有语言)来说都是一个普遍问题。

以上答案是针对 PHP 的,但是 Perl 解决方案就像...一样简单

my $excluderegex =
    '^\n\x20-\x20' .   # Don't Encode Spaces
       '\x30-\x39' .   # Don't Encode Numbers
       '\x41-\x5a' .   # Don't Encode Capitalized Letters
       '\x61-\x7a' ;   # Don't Encode Lowercase Letters

    # in case anything is already encoded
$value = HTML::Entities::decode_entities($value);

    # encode properly to numeric
$value = HTML::Entities::encode_numeric($value, $excluderegex);

【讨论】:

以上是关于XML 解析器错误:未定义实体的主要内容,如果未能解决你的问题,请参考以下文章

JDK XML解析器中的XXE防御

ANT 构建:解析器中的变量:工件未定义

Perl HTML::解析器错误;未定义的子程序 &main::1

正在尝试构建 xerces c++ 解析器。 Netbeans 和命令行构建错误未定义对“xercesc_3_2 ...”的引用

XML中必须进行转义的字符

Web Service(上)