我已经在这个问题上搜索了stackoverflow,并找到了一些主题,但我觉得在这方面我没有真正的答案。
我有一个用户提交的表单,字段的值存储在XML文件中。 XML设置为使用UTF-8编码。
用户将不时地从某处复制/粘贴文本,当我得到“实体未定义错误”时。
我意识到XML只支持少数几个实体,除此之外的任何东西都无法识别 - 因此解析器错误。
从我收集的内容中,我看到了一些选项:
并将其与 
或实际空格交换。我对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这是一个问题。
答案 0 :(得分:22)
我同意这纯粹是编码问题。在PHP中,这就是我解决这个问题的方法:
在将html片段传递给SimpleXMLElement
构造函数之前,我使用html_entity_decode
对其进行了解码。
然后使用utf8_encode()
进一步对其进行编码。
$headerDoc = '<temp>' . utf8_encode(html_entity_decode($headerFragment)) . '</temp>';
$xmlHeader = new SimpleXMLElement($headerDoc);
现在上面的代码不会抛出任何未定义的实体错误。
答案 1 :(得分:15)
您可以对文本进行HTML解析,并仅使用相应的数字实体重新转义(例如:
→ 
)。在任何情况下 - 只需使用未经过清理的用户输入是一个坏主意。
XML中允许使用所有数字实体,只有HTML中已知的数字实体不起作用(&
,"
,<
,{{1}除外},>
)。
但大多数情况下,您只需将实际字符('
→ö
)写入XML文件即可,因此根本不需要使用实体引用。如果你使用DOM API来操纵你的XML(你应该!),这是你最安全的选择。
最后(这是懒惰的开发人员解决方案)你可以构建一个破碎的XML文件(即没有格式良好,有实体错误),只需要pass it through tidy进行必要的修复。这可能有效或可能失败,这取决于 如何破坏整个事物。根据我的经验,整洁是非常聪明的,并且让你逍遥法外。
答案 2 :(得分:4)
1
。我可以找到并替换所有[
?]并将其与[ 
?]或实际空格交换出来。
这是一个强大的方法,但它要求您拥有一个包含所有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));