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

时间:2010-09-27 14:57:57

标签: php xml tinymce entity simplexml

我已经在这个问题上搜索了stackoverflow,并找到了一些主题,但我觉得在这方面我没有真正的答案。

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

用户将不时地从某处复制/粘贴文本,当我得到“实体未定义错误”时。

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

从我收集的内容中,我看到了一些选项:

  1. 我可以找到并替换所有 并将其与 或实际空格交换。
  2. 我可以将代码放在CDATA部分中。
  3. 我可以在XML文件中包含这些实体。
  4. 我对XML文件的处理是,用户可以将内容输入到表单中,然后将其存储在XML文件中,然后该内容在网页上显示为XHTML(使用SimpleXML解析)。 / p>

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

    谢谢, 莱恩

    更新

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

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

    所有这一切的解决方案非常简单:

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

    我想我唯一不理解的是为什么当放置在文本框中时字符仍然显示,因为没有任何东西将它们转换为UTF,但是使用TinyMCE这是一个问题。

6 个答案:

答案 0 :(得分:22)

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

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

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

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

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

答案 1 :(得分:15)

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

XML中允许使用所有数字实体,只有HTML中已知的数字实体不起作用(&amp;&quot;&lt;,{{1}除外},&gt;)。

但大多数情况下,您只需将实际字符(&apos;&ouml;)写入XML文件即可,因此根本不需要使用实体引用。如果你使用DOM API来操纵你的XML(你应该!),这是你最安全的选择。

最后(这是懒惰的开发人员解决方案)你可以构建一个破碎的XML文件(即没有格式良好,有实体错误),只需要pass it through tidy进行必要的修复。这可能有效或可能失败,这取决于 如何破坏整个事物。根据我的经验,整洁是非常聪明的,并且让你逍遥法外。

答案 2 :(得分:4)

  

1。我可以找到并替换所有[&nbsp;?]并将其与[&#160;?]或实际空格交换出来。

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

  

2。我可以将代码放在CDATA部分中。

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

  

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

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

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

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

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

myDiv.innerHTML = myField.value;

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

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

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

答案 3 :(得分:1)

如果你想转换所有字符,这可能对你有所帮助(我写了一段时间):

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

答案 4 :(得分:0)

对于任何解析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);

答案 5 :(得分:0)

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

如果您使用“ SimpleXMLElement”类:

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