我正在使用SimpleXML尝试使用<!ENTITY
声明来解析large XML file。不幸的是,SimpleXML似乎太急于继续扩展这些实体,我宁愿它没有,因为实体符号很短,易于解析,理论上不会在较新版本的文件中更改,而扩展实体是可能改变的英语句子。有没有办法告诉SimpleXML将其敲掉?
在将文件内容传递给XML解析器之前,我曾想过“预解析”XML文件以去除<!ENTITY
位,但这感觉很麻烦,因为它是一个巨大的文件,我'而宁可尽可能地摆弄它。
(请原谅以上任何错误的术语;我在相当长的一段时间内没有完成这个级别的XML工作。)
答案 0 :(得分:3)
看起来似乎如此,但事实并非如此(除非你指定了我认为你不会的标志,尽管你没有在代码中显示你做了什么)。只是如果你使用->asXML()
方法而不是通过to-string-implementation,SimpleXML只能将它返回给你。
让我们举一些例子来说明它是如何工作的。我从DTD中选择了这个简单的实体:
<!ENTITY n "noun (common) (futsuumeishi)">
因此,让我们选择第一个<pos>
元素,因为它包含&n;
个实体:
$xml = simplexml_load_file($file);
$pos = $xml->entry->sense->pos;
变量$pos
现在是<pos>
元素节点的SimpleXMLElement。让我们输出它以查看解析器对&n;
实体的作用:
echo "SimpleXML value (string): ", $pos , "\n"
, "SimpleXML value (XML) : ", $pos->asXML(), "\n";
输出是:
SimpleXML value (string): noun (common) (futsuumeishi)
SimpleXML value (XML) : <pos>&n;</pos>
正如此示例所示,&n;
仍然存在(<pos>&n;</pos>
),只是当您将其作为字符串值(noun (common) (futsuumeishi)
)访问时它将被展开。< / p>
这顺便说一下,XML规范在这里说,解析器是否要扩展这些实体。对于SimpleXML的设计目标,在读取字符串值时完全可以扩展。
您甚至可以通过指定LIBXML_NOENT
选项来控制此行为:
$xml = simplexml_load_file($file, NULL, LIBXML_NOENT);
这实际上会按照您的假设执行,现在实体已展开,XML输出 不再包含实体:
SimpleXML value (string): noun (common) (futsuumeishi)
SimpleXML value (XML) : <pos>noun (common) (futsuumeishi)</pos>
所以现在双重问号如何做你正在寻找的东西?好吧,PHP中的XML解析器实际上有一个实体模型是DOMDocument。它是SimpleXML的姊妹库,内部共享相同的内存对象。以下是没有和LIBXML_NOENT
的两种模式的同一对象的输出(更精确:它唯一的子节点):
Mode 1:
DOMDocument Class : DOMEntityReference
DOMDocument value(XML) : &n;
DOMDocument ->nodeName : n
Mode 2 (LIBXML_NOENT):
DOMDocument Class : DOMText
DOMDocument value(XML) : noun (common) (futsuumeishi)
DOMDocument ->nodeName : #text
这是由以下代码创建的,它应该使给定输出后面的内容更加可见:
$node = dom_import_simplexml($pos);
$doc = $node->ownerDocument;
$entity = $node->firstChild;
echo "DOMDocument Class : ", get_class($entity) , "\n"
, "DOMDocument value(XML) : ", $doc->saveXML($entity), "\n"
, "DOMDocument ->nodeName : ", $entity->nodeName , "\n";
正如所写,它是一个姊妹图书馆,dom_import_simplexml
将$pos
转变为DOMElement
我们需要遍历它的子项,我们知道它是所讨论的实体引用。< / p>
所以现在这开始变得非常有意义:由于SimpleXML不能代表实体引用,它只能提供扩展的字符串值或包含实体的XML。
否则会有什么方法可以区分
的字符串值<pos>&n;</pos>
<pos><![CDATA[&n;]]></pos>
?所以你要求的只是有限的意义。然而,这并不意味着我们无法解决这个问题,因此可以通过从中扩展来欺骗SimpleXML。假设每个仅包含单个实体的子元素应该返回。否则应使用标准的SimpleXML stringyfication:
/**
* Class EntityPreserveXML
*/
class EntityPreserveXML extends SimpleXMLElement
{
/**
* @return string
*/
public function __toString()
{
$dom = dom_import_simplexml($this);
if (
!$dom instanceof DOMElement
|| $dom->childNodes->length !== 1
|| ! $dom->firstChild instanceof DOMEntityReference
) {
return parent::__toString();
}
return $dom->ownerDocument->saveXML($dom->firstChild);
}
}
让我们从上面开始运行我们的例子:
require('EntityPreserveXML.php');
$xml = simplexml_load_file($file, 'EntityPreserveXML');
$pos = $xml->entry->sense->pos;
echo "SimpleXML value (string): ", $pos , "\n"
, "SimpleXML value (XML) : ", $pos->asXML(), "\n";
SimpleXML现在使用扩展类,然后按预期提供:
SimpleXML value (string): &n;
SimpleXML value (XML) : <pos>&n;</pos>
&n;
,因为它是唯一的子项,现在保留在SimpleXMLElement的to-string转换中。但只是因为这个工作并不意味着你应该使用它,它打破了文本形式的解析XML和文档模型意义上的XML之间的编码边界。
可能你只是在寻找DOMDocument?这是一个包含更多细节的模型,如果有的话,你可以使用DOMEntityReference
。