在前面的文章 <<PHP中的XML解析的5种方法>> 中以功能实现为维度实现了XML的解析。 今天我们从另外的维度说说XML解析的两种方式。 一般来说,在PHP中,XML的解析包括DOM,SAX,正则表达式等常规方式。 特别是前面的两种,不管是在PHP中还是在Java等语言中都有使用。
DOM解析器
DOM 是具有平台和语言无关性。它是表示 XML 文档的官方 W3C 标准。 DOM解析器在实现方式是预加载整个文档,并把XML文档转化为一个包含其内容的树。 这个树结构方便开发人员在其中寻找特定信息,并可以调用树的一些操作很容易的添加和修改树中的元素。
在PHP中,它在形式上是基于对象的存储,然而在本质上是存储在一堆结构体中, 在对象与对象的之间是以一种类似于父子的概念关系存在,从而在整体上构成了一个树的结构。
由于DOM解析器是预加载的,所以整个文档的结构在内存中是持久存在的。因此可以在其生命周期中随时修改它,以便应用程序能对数据和结构作出更改。 它还可以在任何时候在整个树结构中上下导航,并且DOM解析器使用起来比较简单。 但是,同样因为是预加载的方式,需要处理整个 XML 文档,所以对性能和内存的要求比较高。 当遇到特别大的文档时,解析和加载整个文档可能会很慢且很耗资源, 此时我们就需要另外一种方式,比如一边读取一边处理,又或者类似于SAX基于事件的模型。
在PHP中使用DOM解析器是基于DOMDocument类来实现。具体的实现请移步之前的文章: <<PHP中的XML解析的5种方法>>
SAX解析器
SAX是simple API for XML的简写,与DOM不同,它并不进行预加载操作,而是一边扫描文档,一边解析。 SAX解析器 采用了基于事件的模型,它在解析 XML 文档的时候可以触发一系列的事件, 当发现给定的tag的时候,它可以激活一个回调函数,告诉该函数指定的标签已经找到。 SAX解析器 对内存的要求通常会比较低,因为它让开发人员自己来决定所要处理的tag。 特别是当开发人员只需要处理文档中所包含的部分数据时,SAX解析器 这种扩展能力得到了更好的体现。 但用 SAX 解析器的时候编码工作会比较困难,而且很难同时访问同一个文档中的多处不同数据。 看一个PHP中使用SAX解析器的例子。我们使用Google天气API的XML文档。
API地址:http://www.google.com/ig/api?weather=shenzhen
<?php
/**
* 简单的Google天气SAX解析器
* 解析http://www.google.com/ig/api?weather=shenzhen中将来几天的天所情况
*/
class weatherSaxParser {
private $_parser;
private $_xmlData;
/**
* 当前的Tag
* @var <type>
*/
private $_tag;
private $_weather;
/**
* 保存天气的数组的key
* @var <type>
*/
private $_key;
private $_attributes;
/**
*需要解析的标签集合
* @var <type>
*/
private $_parseTags = array('low', 'day_of_week', 'high', 'condition');
public function __construct() {
$this->_key = 0;
$this->_parser = xml_parser_create();
xml_set_object($this->_parser, $this);
xml_set_element_handler($this->_parser, 'tagStart', 'tagEnd');
xml_set_character_data_handler($this->_parser, 'tagContent');
}
public function setXmlData($xml) {
$this->_xmlData = $xml;
}
/**
* 执行解析操作
*/
public function run() {
xml_parse($this->_parser, $this->_xmlData);
}
/**
* 标签开始回调函数
* @param <type> $parser
* @param <type> $tagName
* @param <type> $attributes
*/
public function tagStart($parser, $tagName, $attributes = NULL ) {
$this->_tag = strtolower($tagName);
$this->_attributes = $attributes;
if ($this->_tag == 'forecast_conditions') {
$item = array();
$this->_weather[$this->_key] = $item;
$this->_key++;
}
if ($this->checkTag()) {
if (empty($this->_weather[$this->_key - 1][$this->_tag])) {
$this->_weather[$this->_key - 1][$this->_tag] = $this->_attributes['DATA'];
}
}
}
public function tagEnd($parser, $tagName ) {
$this->_tag = NULL;
$htis->_attributes = NULL;
}
public function tagContent($parser, $content ) {
if ($this->checkTag()) {
$this->_weather[$this->_key - 1][$this->_tag] = $content;
}
}
public function __destruct() {
xml_parser_free($this->_parser);
}
public function checkTag() {
return in_array($this->_tag, $this->_parseTags) && $this->_key > 0;
}
public function getWeather() {
return $this->_weather;
}
}
$fp = fopen('weather.xml', 'r');
$saxParser = new weatherSaxParser();
while ($data = fread($fp, 4096)) {
$saxParser->setXmlData($data);
$saxParser->run();
}
print_r($saxParser->getWeather());
unset($saxParser);
以上针对XML文档中’low’, ‘day_of_week’, ‘high’, ‘condition’, ‘forecast_conditions’等标签进行处理。 在SAX解析器中,对于文档的遍历也是依赖于文件的行读取操作。
从上面的例子我们也可以看出SAX解析器的一些缺点:
- SAX解释器不允许对XML文件随机存取,只能顺序读取
- SAX解释器中元素之间的遍历困难,在多个标签间移动比较困难
- SAX是解析一个节点后回调一个方法,把该节点相关信息传送个调用者,然后丢弃这些信息,继续解析下一个节点。它不会预存储整个XML文档,也不会在解析后保存任何解析结果。
- SAX的修改XML能力差
结论
DOM 采用建立树形结构的方式访问 XML 文档,它体现了预处理的编程优化思想。这对于一次获取多次查询或修改的情况较适用,并且DOM具有良好的接口定义,编程较方便,一般来说,选择DOM会舒服很多。 但是SAX也有其存在的理由,SAX 采用的事件模型,它体现了只取所需的优化思想。这对于只处理一次或数据量巨大导致无法预加载时有更好的性能。
DOM与SAX有点类似于PHP中读取文件操作file_get_contents和fopen/fread/feof的组合。 file_get_contents的使用比较方便,并且可以一次性将所有数据取出来,仅以后调用。 fopen/fread/feof组合操作则是打开文件可以一段一段的处理。而在SAX中可能也会调用fopen/fread/feof组合。
正所谓有得有失,重点在一个平衡和取舍,根据实际情况使用合适的技术。
参考资料
http://www.ibm.com/developerworks/cn/opensource/os-xmldomphp/ http://hannoi2009.blog.163.com/blog/static/12282842820097157152651/