我正在从我的数据库中检索一些html字符串,我想将这些字符串解析为我的DOMDocument。问题是,DOMDocument以特殊字符发出警告。
警告: DOMDocumentFragment :: appendXML() [domdocumentfragment.appendxml]: 实体:第2行:解析器错误:实体 'nbsp'未在中定义 page.php文件 在第189行
我想知道为什么,我想知道如何解决这个问题。这是我页面的一些代码片段。我该如何解决这些警告?
$doc = new DOMDocument();
// .. create some elements first, like some divs and a h1 ..
while($row = mysql_fetch_array($result))
{
$messageEl = $doc->createDocumentFragment();
$messageEl->appendXML($row['message']); // gives it's warnings here!
$otherElement->appendChild($messageEl);
}
echo $doc->saveHTML();
我还发现了一些关于验证的内容,但是当我应用它时,我的页面将不再加载。我试过的代码是这样的。
$implementation = new DOMImplementation();
$dtd = $implementation->createDocumentType('html','-//W3C//DTD XHTML 1.0 Transitional//EN','http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd');
$doc = $implementation->createDocument('','',$dtd);
$doc->validateOnParse = true;
$doc->formatOutput = true;
// in the same whileloop, I used the following:
$messageEl = $doc->createDocumentFragment();
$doc->validate(); // which stopped my code, but error- and warningless.
$messageEl->appendXml($row['message']);
提前致谢!
答案 0 :(得分:6)
XML中没有
。唯一定义了实际名称(而不是使用数字引用)的字符实体是&
,<
,>
,"
和'
。
这意味着你必须使用不间断空格的数字等价物,即 
或(十六进制) 
。
如果您尝试将HTML保存到XML容器中,请将其另存为文本。 HTML和XML可能看起来很相似,但它们非常不同。 appendXML()
期望格式良好的XML作为参数。请改用nodeValue
属性,它将对您的HTML字符串进行XML编码,而不会发出任何警告。
// document fragment is completely unnecessary
$otherElement->nodeValue = $row['message'];
答案 1 :(得分:5)
这是一个棘手的问题,因为它实际上是多个问题。
就像Tomalak指出的那样,XML中没有
。所以你做了正确的事情来指定DOMImplementation,因为在XHTML中有
。但是,要让DOM知道文档是XHTML,您需要加载并验证DTD。 DTD位于
http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd
但是因为除了在请求中发送了UserAgent之外,每天有数百万个请求到W3C decided to block access to the page {{}}}。要提供UserAgent,您必须创建自定义流上下文。
在代码中:
// make sure DOM passes a User Agent when it fetches the DTD
libxml_set_streams_context(
stream_context_create(
array(
'http' => array(
'user_agent' => 'PHP libxml agent',
)
)
)
);
// specify the implementation
$imp = new DOMImplementation;
// create a DTD (here: for XHTML)
$dtd = $imp->createDocumentType(
'html',
'-//W3C//DTD XHTML 1.0 Transitional//EN',
'http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd'
);
// then create a DOMDocument with the configured DTD
$dom = $imp->createDocument(NULL, "html", $dtd);
$dom->encoding = 'UTF-8';
$dom->validate();
$fragment = $dom->createDocumentFragment();
$fragment->appendXML('
<head><title>XHTML test</title></head>
<body><p>Some text with a entity</p></body>
'
);
$dom->documentElement->appendChild($fragment);
$dom->formatOutput = TRUE;
echo $dom->saveXml();
这还需要一些时间才能完成(不要问我为什么)但最后,你会得到(重新格式化为)
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC
"-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>XHTML test</title>
</head>
<body>
<p>Some text with a entity</p>
</body>
</html>
答案 2 :(得分:0)
我确实看到了有问题的问题,而且这个问题已经得到了回答,但如果我可以提出我过去处理类似问题的想法。
可能是因为您的任务需要在生成的XML中包含来自数据库的标记数据,但可能需要解析也可能不需要解析。如果它只是包含的数据,而不是XML的结构化部分,则可以在CDATA section(s)中放置数据库中的字符串,从而有效地绕过了此阶段的所有验证错误。
答案 3 :(得分:0)
这是另一种方法,因为我们不想降低网络请求的速度(或者根本不希望任何来自用户输入的网络请求):
<?php
$document = new \DOMDocument();
$document->loadHTML('<html><body></body></html>');
$html = '<b>test </b>';
$fragment = $document->createDocumentFragment();
$html = '<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE document [
<!ENTITY nbsp " " >
]>
<document>'.$html.'</document>';
$newdom = new \DOMDocument();
$newdom->loadXML($html, LIBXML_HTML_NOIMPLIED | LIBXML_NOCDATA | LIBXML_NOENT | LIBXML_NONET | LIBXML_NOBLANKS);
foreach ($newdom->documentElement->childNodes as $childnode)
$fragment->appendChild($fragment->ownerDocument->importNode($childnode, TRUE));
$document->getElementsByTagName('body')[0]->appendChild($fragment);
echo $document->saveHTML();
在这里,我们将DTD的相关部分,特别是latin1 entity definitions作为内部DOCTYPE定义包括在内。然后,将HTML内容包装在document元素中,以便能够处理一系列子元素。然后将已解析的节点导入并添加到目标DOM中。
我们的实际实现使用file_get_contents从本地文件加载包含所有实体定义的DTD。
答案 4 :(得分:-1)
虽然聪明可能是一个不错的选择(为什么第14次发明轮子?),etranger可能有一个观点。在某些情况下,您不希望使用像一个完整的新(和未经研究的)包这样的东西,但更像是想要从数据库发布一些数据,这些数据恰好包含XML解析器存在问题的HTML内容。
警告,以下是一个简单的解决方案,但除非你确定你可以逃脱它,否则不要这样做! (当我在截止日期前大约2个小时没有时间学习时,我做了这个,留下了一些像聪明的工具......)
在将字符串粘贴到appendXML函数之前,请通过preg_replace运行它。例如,替换所有&amp; NBSP; [some_prefix] _nbsp的字符。然后,在显示html的页面上,以相反的方式执行。
和Presto! =)
示例代码: 将文本放入文档片段的代码:
// add text tag to p tag.
// print("CCMSSelTextBody::getDOMObject: strText: ".$this->m_strText."<br>\n");
$this->m_strText = preg_replace("/ /", "__nbsp__", $this->m_strText);
$domTextFragment = $domDoc->createDocumentFragment();
$domTextFragment->appendXML(utf8_encode($this->m_strText));
$p->appendChild($domTextFragment);
// $p->appendChild(new DOMText(utf8_encode($this->m_strText)));
解析字符串并编写html的代码:
// Instantiate template.
$pTemplate = new CTemplate($env, $pageID, $pUser, $strState);
// Parse tag-sets.
$pTemplate->parseTXTTags();
$pTemplate->parseCMSTags();
// present the html code.
$html = $pTemplate->getPageHTML();
$html = preg_replace("/__nbsp__/", " ", $html);
print($html);
想出一个更强大的替代品可能是一个好主意。 (如果你坚持彻底:在time()值上做一个md5,并将其结果硬编码为前缀。所以就像在第一个片段中一样:
$this->m_strText = preg_replace("/ /", "4597ee308cd90d78aa4655e76bf46ee0_nbsp", $this->m_strText);
在第二个:
$html = preg_replace("/4597ee308cd90d78aa4655e76bf46ee0_nbsp/", " ", $html);
对于您需要规避的任何其他标签和内容,请执行相同的操作。
这是一个黑客,而不是任何想象力的代码。但它保存了我的直播,并希望与其他遇到这个特定问题的人分享它。
使用上述内容需要您自担风险。