|
|
概述
在网上有许多php的XML教程,但是只有少数介绍如何使用DOM来解析XML。我想利用这个机会展示一下在PHP编程中除了广泛使用的SAX实现方式外,还有另一种选择。
DOM(Document Object Model,文档对象模型)和SAX(Simple API for XML,XML简单应用程序接口)在如何解析XML上有不同的方法。SAX引擎完全是事件驱动的。当它遇到一个标记时,它就调用一个适当的函数来处理它。这使得SAX非常快速和有效。然而他给你的感觉就象被套在一个没完没了的循环里面。你发现自己使用了太多的全局变量和条件语句。
另一方面,DOM方法稍稍对内存有些敏感。它把整个XML文档以层次化的结构方式装载到内存里。这就是说,所有的数据组成了一个家族树,它们对编程者来说都是可用的。这种方法更直观,更容易使用,也提供了更多的可读性。
为了使用DOM函数,你必需在配置PHP时,使用'--with-dom'参数。它们不是标准配置的组成部分,这里有一个简单的编译方式。
%> ./configure --with-dom --with-apache=../apache_1.3.12 %> make %> make install | 译注:在Win32平台上支持DOM做法是这样的。首先,将下载包中dlls目录下的php_domXML.dll文件复制到系统目录下。NT、Win2K是system32目录,9X是system目录。其次,修改php.ini文件。将"Paths and Directories"中的extension_dir参数指向php_domXML.dll所在的路径,如extension_dir = C:\Winnt\system32;将"Dynamic Extensions"中extension=php_domXML.dll前的注释去掉。
DOM如何构造XML
因为DOM装载一个完整的字符串或文件到内存中作为一个树,这使我们可以将这些数据作为一个整体进行操作。我们拿这个XML文档作为一个例子。
<?XML version="1.0"?>
<book type="paperback"> <title>Red Nails</title> <price>$12.99</price> <author> <name first="Robert" middle="E" last="Howard"/> <birthdate>9/21/1977</birthdate> </author> </book> | 数据将象这样被组织起来
DomNode book | |-->DomNode title | | | |-->DomNode text | |-->DomNode price | | | |-->DomNode text | |-->DomNode author | |-->DomNode name | |-->DomNode birthdate | |-->DomNode text | 任何被标记封闭起来的文本都是它们自身的节点。举个例子来说,"Red Nails"是title的子节点,"$12.99"是price的子节点。
在DOM中使用的对象
你可能会觉得困惑,什么是一个DomNode。我们从这里开始讨论包含在DOM模型中的对象。DOM定义了五种对象:DomDocument, DomNode, DomAttribute, DomDtd, 和 DomNamespace。我们将把主要注意力集中在DomDocument和DomNode对象,因为他们是最常用的。
Node对象
下面是一个DomNode对象所包含内容的概览。
class DomNode properties: name content type methods: lastchild() children() parent() new_child( $name,$content ) getattr( $name ) setattr( $name,$value ) attributes() | properties需要一些详细的说明。
·name 属性实际上是节点标记的名称。一个引用title标记的节点可能就是用'title'作为节点名。
·content 属性通常是空的。然而文本型节点使用这个属性来保存文本。
·type 属性是个常数,它明确定义了这个节点是什么类型的对象。有一些DomNode对象的类型。这些类型常数的列表可以从http://www.php.net/manual/ref.domXML.php在线获得。例如,一个包含文本内容的节点就可能有一个XML_TEXT_NODE的类型。
methods也需要解释一下。
·lastchild() 返回一个节点的最后一个子节点。
·parent() 返回一个节点的父节点。例如,我们这里title节点的父节点就是'book'。
·children() 返回一个包含某节点所有子节点的数组。例如,author节点的children就是'name'和'birthdate'。
·new_child() 增加一个新的子节点,包括一个名称和一些作为参数的内容。
·getattr()和setattr()都是用于处理属性的。一个是取得属性值,一个是设置属性值。
·attributes() 返回一个DomAttribute对象的数组。
DomDocument对象
DomDocument对象也是重要的。
class DomDocument properties: version encoding standalone type methods: root() children() add_root( $node ) dtd() dumpmem()
| properties(属性)的名字就可以解释自身的含义。
·'version' 指文档的XML版本号。
·'encoding' 指文本的编码。
·'standalone' 是一个布尔值,它决定文档是否独立。
method(方法)也是相当简单的。
·root() 返回文档的根节点。如果我们载入前面的XML范例作为一个DomDocument对象,那么根节点就是'book'。
·children() 与DomNode中的children一样。
·add_root() 在XML文档中增加一个新的根节点。如果你想用一个其他节点来代替'book'节点,那么你就要用到这个方法了。
·dtd() 返回XML文档的dtd。
·dumpmem() 返回XML数据的字符串表示。译注:dumpmem()方法将整个DomDocument对象串行化为一个字符串,并返回。
由XMLtree()返回的DomDocument对象
XMLtree(),返回另一种类型的DomDocument对象,它可能会给你带来麻烦。这个对象没有方法,它用属性代替了方法。它有一个真正的树形结构。
class DomDocument properties: version encoding standalone name content type attributes children | 它很容易使用。例如,用不着使用一个方法去得到一个节点的子节点,只要访问它的'children'属性就可以了。同样,'children'和'attributes'属性都是数组。
其他的对象
我将列出其他对象以及它们的属性和方法作为参考。在这篇文章中,我们将用不到它们。
class Attribute properties: name content methods: name()
class Dtd properties: extid sysid name class Namespace |
使用对象
DOM模型只有三个函数,XMLdoc(),XMLdocfile()和XMLtree()。剩下的时间,我们将用这些对象进行处理。上面的函数都返回DomDocument对象。这有一些例子,关于如何装载XML数据到你的php脚本。
<?php
# 使用下面两个方法中的任一个,从一个字符串装载XML
$doc = XMLdoc( $XMLstr ); $tree = XMLtree( $XMLstr );
# 从一个文件装载XML $doc = XMLdocfile( $XMLfile );
?> | 如果XML不能被正确解析,那么这些函数都会掷出一个错误。DOM不会为你验证XML文件的正确性。你必需用其他的方式来完成这一点。或许可以通过其他的程序来做,如XMLlint。译注:微软IE中内嵌XML解析器,只要用IE来浏览XML文档,就可以验证文档的有效性了。
一个简单的例子
让我们用一个简单的例子,将前面提到的东西联系起来。
<?php
# 生成一个XML范例文档,以进行演示 $XMLstr = "<" . "?" . "XML version=\"1.0\"" . "?" . ">"; $XMLstr .= " <employee> <name>Matt</name> <position type=\"contract\">Web Guy</position> </employee> ";
# 装载XML数据($doc成为一个DomDocument对象的实例) $doc = XMLdoc($XMLstr);
# 得到根节点"employee" $employee = $doc->root();
# 得到employee节点的子节点("name","position") $nodes = $employee->children(); # 我们打算使用"position"节点 # 因此我们必需反复通过employee的子节点来搜索它 while ($node = array_shift($nodes)) { if ($node->name == "position") { $position = $node; break; } }
# 得到position的类型属性 $type = $position->getattr("type"); # 得到被封闭在position标记中的文本 # 移动到position子节点的第一个子节点 # 译注:这里的用法可以参考第一部分“DOM如何构造XML”的最后一段。 $text_node = array_shift($position->children());
# 访问这个文本节点的内容属性 $text = $text_node->content;
# 输出position和type echo "position: $text<BR>"; echo "type: $type";
?> | 这个例子将产生下面的输出。
position: Web Guy type: contract | 上面例子中,while循环实际上是寻找position节点。这里的employee节点真正有5个子节点:三个text(文本),一个name,一个position。这个文本节点包含在行末尾的换行。这一点开始看起来比较奇怪,但是DOM将任何的字符串(即使是那些只包含空白部分的)都作为text(文本),并为它们都创造一个节点。 如果你为了确保employee节点只有两个子节点,那么你必需象这样写XML项目。
<employee> <name>Matt</name> <position type="contract">Web Guy</position> </employee> | 一个更长的例子
下面是一个更长的例子,关于如何从一个XML文档中提取信息。比如,我们有一个employees.XML文件,它包含employee信息。
<?XML version="1.0"?>
<employees company="zoomedia.com"> <employee> <name>Matt</name> <position type="contract">Web Guy</position> </employee>
<employee> <name>George</name> <position type="full time">Mad Hacker</position> </employee>
<employee> <name>Wookie</name> <position type="part time">Hairy SysAdmin</position> </employee> </employees>
| 下面展示如何在php脚本提取这个信息。
<?php
# 遍历一个节点数组,寻找一个文本节点,并返回它的内容 function get_content($parent) { $nodes = $parent->children(); while($node = array_shift($nodes)) if ($node->type == XML_TEXT_NODE) return $node->content; return ""; }
# 得到一个特定节点的内容 function find_content($parent,$name) { $nodes = $parent->children(); while($node = array_shift($nodes)) if ($node->name == $name) return get_content($node); return ""; }
# 得到一个特定节点的属性 function find_attr($parent,$name,$attr) { $nodes = $parent->children(); while($node = array_shift($nodes)) if ($node->name == $name) return $node->getattr($attr); return ""; }
# 载入XML文档 $doc = XMLdocfile("employees.XML") or die("What employees?");
# 得到根节点(employees) $root = $doc->root();
# 得到employees的子节点数组,它包含了每一个employee节点 $employees = $root->children();
# 在数组中移动,输出一些emloyee数据 while($employee = array_shift($employees)) { if ($employee->type == XML_TEXT_NODE) continue;
$name = find_content($employee,"name"); $pos = find_content($employee,"position"); $type = find_attr($employee,"position","type");
echo "$name the $pos, $type employee<br>"; }
?> | 你可以从你的浏览器上看到下面的输出。
Matt the Web Guy, contract employee George the Mad Hacker, full time employee Wookie the Hairy SysAdmin, part time employee | 另一个例子(增加数据)
因为XML被载入到内存作为一个树,所以我们能够很容易地操作这些数据。必要时,我们能够增加分支或节点。
比方说,我们想在XML文件中增加一个employee(售员)。
<?php
# 生成子节点的函数 function make_node($parent,$name,$content) { # 增加一个新子节点到父节点 $parent->new_child($name,$content);
# 返回新增加的子节点 return $parent->lastchild(); }
# 载入XML文件,得到根节点 $doc = XMLdocfile("employees.XML") or die("Do you even have any employees?"); $root = $doc->root();
# 赋予新employee(雇员)一个名字 $newguy = make_node($root,"employee","");
# 增加新成员的名字 make_node($newguy,"name","New Guy");
# 增加他的职位 $position = make_node($newguy,"position","Backup Gnome");
# 设置'type'属性 $position->setattr("type","intern");
# 将我们改变过的XML文档输出到浏览器 echo $doc->dumpmem();
?> | 这个脚本将会打印XML文档到浏览器上,所以你可能愿意使用'查看源文件'来看这些数据。
结论
这些差不多就是DOMXML的全部了。它是一个简单的方法,在脚本中解析并操作XML。希望这篇文章能吸引更多的人关注PHP中的这个角落。
|
|