您的位置:首页 > 编程语言 > PHP开发

[转]php使用DOM处理xml文档

2008-05-31 18:35 851 查看

访问树中节点

树中节点的访问不限制在往下一级,你可以访问当前节点的兄弟节点,父节点,甚至任何一个可能被访问到的节点。

访问子节点

子节点是当前节点的直接后代。简而言之,所有直接处于当前节点的下一级别的节点都是当前节点的子节点。例如,一个元素节点可能有包含(并不局限于此)注释节点,文本节点和一些其它的元素节点。属性节点只有一个子节点,这个子节点是一个包含属性节点的值的文本节点。文档节点可以包含注释节点,PIs金额一个单独的元素节点来作为子节点。子节点的类型很多时候是由当前节点决定的。可以使用hasChildNodes()方法来检查一个节点是否包含子节点,这个方法将返回一个布尔值来表明该节点是否有子节点。

可以用childNodes属性返回一个包含所有子节点的DOMNoteList对象。DOMNoteList是一个可以循环的对象。可以使用item属性来取得一个DOMNoteList中的一个特定的节点或者利用PHP中的循环函数来访问。比如

if ($root->hasChildNodes()) {

$children = $root->childNodes;

foreach($children as $node) {

print $node->nodeName."/n";

}

}

这段代码先返回了文档节点$root的子节点,然后利用foreach来遍历DOMNodeList对象。输出结果如下
#text
bookinfo
#text
preface
#text
chapter
#text

book元素包含了三个子原属,而且也包含了一些换行符或制表符之类的空白,这些空白在加载XML的时候没有去掉,因此输出结果中有#text。如何在没有去掉XML空白的情况下在结果中去掉#text呢,nodeType属性派上用场了。例如:

foreach($children as $node) {

if ($node->nodeType != XML_TEXT_NODE) {

print $node->nodeName."/n";

}

}

这样就可以去掉那些文本节点(text node),输出结果也没有#text字符了。

———————————————–
接下来不翻译了,改成笔记,翻译的废话太多了 >. 读者可自己运行代码后思考一下,这样更容易上手 ^^
———————————————–

访问特定节点

如果要访问特点节点可以使用节点的标签名。可使用的方法有getElementsByTagName()和getElementsByTagNameNS(),被访问的节点只能包含在文档节点(document node)和元素节点(element node)中,也就是说,要访问的节点必须是基于DOMDocument或DOMElement类。比如文档节点$dom,可以用getElementsByTagName()方法来取得文档中的所有title节点:

$elements = $dom->getElementsByTagName("title");

$length = $elements->length;

for ($x=0;$x < $length;$x++) {

print "Element Value: ".$elements->item($x)->nodeValue."/n";

}

getElementsByTagName()返回了一个DOMNodeList对象$elements,包含了$dom领域内的所有title元素。length属性返回了DOMNodeList对象中元素的个数。

可以使用通配符*作为参数来取得所有的元素:

$preface = $root->getElementsByTagName("preface");

$elements = $preface->item(0)->getElementsByTagName("*");

$length = $elements->length;

for ($x=0;$x < $length;$x++) {

print "Element Name: ".$elements->item($x)->nodeName."/n";

print "Element Value: ".$elements->item($x)->nodeValue."/n";

}

这段代码通过文档元素$root以一个DOMNodeList对象返回了$root之内的所有preface元素并赋值给$preface。接着,返回取得的DOMNodeList对象中的第一个值,然后直接执行getElementsByTagName(”*”)。所有$preface内的元素都以DOMNodeList对象返回到$elements中。最后利用for循环输出所有的元素名称及元素值。输出结果如下:
Element Name: title
Element Value: The DOM Tree
Element Name: para
Element Value: An example DOM Tree using DocBook.

在处理有命名空间的文档时,可以使用getElementsByTagNameNS()方法来返回指定的命名空间的元素。这个方法有两个参数,第一个是命名空间URI,第二个是元素标签的名称。命名空间URI参数也支持通配符*。可以用通配符来取得任一个命名空间的所有元素:

$result = $dom->getElementsByTagNameNS("*", "*");

这行代码返回了一个DOMNodeList对象$result,返回了文档中有命名空间的所有元素。

访问属性(Accessing Attributes)

跟其它节点类型一样,节点的属性(attribute)继承了DOMNode类中的方法和属性(property),但是不能像其它节点一样访问它。
可以用hasAttributes()方法来检查一个节点是否有属性,用attributes属性(property)来取得节点的所有属性, hasAttributes()和attributes都是在DOMNode类中定义的,因此可以在所有的节点中使用,但是只有在DOMElement对象中是由才会返回有意义的值。

if ($root->hasAttributes()) {

$attributes = $root->attributes;

foreach($attributes as $attr) {

print "Attribute Name: ".$attr->nodeName."/n";

print "Attribute Value: ".$attr->nodeValue."/n";

}

}

在这段代码中,如果$root所指代的节点存在属性的话hasAttributes()返回true,接着利用attributes属性返回一个DOMNamedNodeMap对象给$attributes。DOMNamedNodeMap跟DOMNodeList一样,对是可以循环的。输出结果如下:
Attribute Name: lang
Attribute Value: en

与DOMNodeList不同的是DOMNamedNodeMap可以使用名称来访问,而不只是使用位置。比如:

$attr = $attributes->getNamedItem("lang");

print "Attribute Name: ".$attr->nodeName."/n";

print "Attribute Value: ".$attr->nodeValue."/n";

if ($attributes->length > 0) {

$attr = $attributes->item(0);

print "Attribute Name: ".$attr->nodeName."/n";

print "Attribute Value: ".$attr->nodeValue."/n";

}

这两段代码输出结果是一样的。

访问特定属性

用DOMNameNodeMap对象来访问属性只是访问属性的方法之一。DOMElement类提供了一些用来访问指定属性的方法,getAttribute(), getAttributeNode(), getAttributeNS(), and getAttributeNodeNS()

/* Access lang attribute value directly */

print "Attribute Value: ".$root->getAttribute("lang")."/n";

/* Return the lang attribute node and access the returned attribute node */

$attr = $root->getAttributeNode("lang");

print "Attribute Value: ".$attr->nodeValue."/n";

这两段代码的输出结果是一样的。第一段代码返回了lang属性后直接输出;第二段代码先返回lang属性节点,接着输出节点值。

尽管Listing 6-1 中的文档没有使用命名空间,但是还是可以使用命名空间的方法来访问。

print "Attribute Value: ".$root->getAttributeNS(NULL, "lang")."/n";

$attr = $root->getAttributeNodeNS(NULL, "lang");

print "Attribute Value: ".$attr->nodeValue."/n";

第一个参数是要访问的命名空间URI,因为Listing 6-1没有命名空间,因此指定为NULL。

创建和编辑树

文档节点(Document Nodes)

本章早些时候介绍了几种创建DOMDocument对象的方法。创建的对象包含一个文档类型声明,你可以使用DOMImplementation累来创建一个DOMDocument对象,首先创建一个DOMDocType对象来作为DOMImplementation的参数。 DOMImplementation对象中的方法可以使用静态方式访问。比如:

$doctype = DOMImplementation:: createDocumentType("book",

"-//OASIS//DTD DocBook XML V4.1.2//EN",

"http://www.oasis-open.org/docbook/xml/4.1.2/docbookx.dtd");

$dom = DOMImplementation:: createDocument(NULL, "book", $doctype);

指定编码

$dom->encoding = "UTF-8";

指定编码只影响输出的文档,不影响创建文档过程中的文档编码。

元素节点

创建元素

有两种方法可以用来创建元素,一是使用DOMDocument对象中createElement()和createElementNS()方法,二是利用DOMElement直接实例化一个DOMElement对象。

创建一个bookinfo元素

$bookinfo = $dom->createElement("bookinfo");

这段代码返回了一个名为”bookinfo”的DOMElement对象类型元素给$bookinfo变量。

也可以创建一个包含内容的元素

$bititle = $dom->createElement("title", "DOM in PHP 5");

虽然已经创建了两个元素,但是它们跟文档没有直接关系,是独立的两个元素。

创建一个带有命名空间的元素

$biauthor = $dom->createElementNS(NULL, "author");

$trash = $dom->createElementNS("http://www.example.com/trash", "tr:trash");

直接实例化DOMElement对象来创建元素

$firstname = new DOMElement("firstname", "Rob");

$surname = new DOMElement("surname", "Richards");

$nsElement = new DOMElement("nse:myelement", NULL, "http://www.example.com/ns");

第三行代码的NULL说明不指定值给所创建的元素。

插入元素节点

插入元素节点的方法是从DOMNode类继承的,它们不仅可以用于元素节点,还可以用于其它类型的节点。

$dom->documentElement->appendChild($bookinfo);

appendChild()方法有一个被用来指定为子节点的对象作参数,并返回所创建的节点。新插入的节点作为当前节点的最后一个子节点插入。

在插入author元素前可以先在author元素中插入firstname和surname节点。当将author节点插入文档树中时,firstname和surname也被插入文档中:

$biauthor->appendChild($surname);

$biauthor->insertBefore($firstname, $surname);

上面的代码也可以这么写

$biauthor->appendChild($firstname);

$biauthor->appendChild($surname);

然后将author插入bookinfo节点:

$bookinfo->appendChild($biauthor);

属性节点

可以使用DOMElement中的方法操作属性节点,使用DOMDocument中的方法来直接实例化一个属性节点或使用DOMElement中的方法来创建,也可以使用DOMNode和DOMElement中的方法来插入和移动属性节点。

使用DOMDocument中的createAttribute方法创建属性节点

/* Equivalent methods for creation of lang attribute */

$lang = $dom->createAttribute("lang");

$lang = $dom->createAttributeNS(NULL, "lang");

使用这种方法创建必须另外再给属性赋值,可以使用DOMElement中的nodeValue属性或DOMAttr中的value属性来指定一个值,如:

/* Equivalent calls to set the value for the lang attribute to "en" */

$lang->nodeValue = "en";

$lang->value = "en";

使用DOMAttr创建一个属性节点对象,并直接赋值:

$lang = new DOMAttr("lang", "en");

创建好的属性节点可以想插入元素节点一样插入到一个节点中,但插入的结果不是插入一个子节点,而是一个属性:

/* Equivalent methods for inserting an attribute */

$bookinfo->appendChild($lang);

$bookinfo->insertBefore($lang, NULL);

也可以使用DOMElement中的setAttributeNode()和setAttributeNodeNS()方法在一个节点中插入一个属性。这两个方法必须有一个DOMAtrr对象类型的属性节点作为参数。

/* Equivalent calls for this document as no namespaces are being used */

$oldlang = $bookinfo->setAttributeNode($lang);

$oldlang = $bookinfo->setAttributeNodeNS($lang);

DOMElement类有两个可以直接给一个节点创建属性的方法,setAttribute()和setAttributeNS()。利用这两个方法可以在不创建DOMAttribute对象的情况下就创建一个属性:

/* Equivalent calls to create the lang attribute with value "en" */

$bookinfo->setAttribute("lang", "en");

$bookinfo->setAttributeNS(NULL, "lang", "en");

文本节点

文本节点非常简单,因为它们没有子节点和属性,也就是说它们只包含文本。

创建和插入文本节点

可以利用DOMDocment对象的createTextNode()方法来创建一个文本节点:

/* Equivalent creation of DOMText objects */

$yeartxt = $dom->createTextNode("2005");

$yeartxt = new DOMText("2005");

可以在一行代码中创建节点并将节点插入元素,最后返回创建的节点:

/* Create and Append a copyright element */

$copyright = $bookinfo->appendChild(new DOMElement("copyright"));

下面的代码实现了跟上一段代码一样的功能:

/* Create year element */

$year = $dom->createElement("year");

/* Append text node to set content */

$year->appendChild($yeartxt);

$copyright->appendChild($year);

另外一种创建文本节点的方法:

/* Append a newly created holder element with content "Rob Richards" */

$copyright->appendChild(new DOMElement("holder", "Rob Richards"));

处理文本

DOMText类是从DOMCharacterData类派生出来的,因此,这两个类中的方法都可以用来处理文本。

/* If content is not whitespace then ... */

if (! $yeartxt->isElementContentWhitespace()) {

/* Print substring at offset 1 and length 2: 00 */

print $yeartxt->substringData(1,2)."/n";

/* Append the string -2006 to the content and print output: 2005-2006 */

$yeartxt->appendData("-2006");

print $yeartxt->nodeValue."/n";

/* Delete content at offset 4 with length of 5 and print output: 2005 */

$yeartxt->deleteData(4,5);

print $yeartxt->nodeValue."/n";

/* Insert string "ABC" at offset 1 and print output: 2ABC005 */

$yeartxt->insertData(1, "ABC");

print $yeartxt->nodeValue."/n";

/* Replace content at ofset 1 with length of 3 with an empty string: 2005 */

$yeartxt->replaceData(1, 3, "");

print $yeartxt->nodeValue."/n";

}

现在,上面所有对节点的操作创建了一个如下所示的XML文档:

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.1.2//EN"

"http://www.oasis-open.org/docbook/xml/4.1.2/docbookx.dtd">

<book>

<bookinfo lang="en">

<title>DOM in PHP 5</title>

<author>

<firstname>Rob</firstname>

<surname>Richards</surname>

</author>

<copyright>

<year>2005</year>

<holder>Rob Richards</holder>

</copyright>

</bookinfo>

</book>

其它类型的节点

/* Create a DOMDocumentFragment */

$frag = $dom->createDocumentFragment();

$frag = new DOMDocumentFragment();

/* Create DOMComment */

$comment = $dom->createComment("this is a comment");

$comment = new DOMComment("this is a comment");

/* Results in <!-- this is a comment --> */

/* Create DOMCDATASection */

$cdata = $dom->createCDATASection("<html></html");

$cdata = new DOMCDATASection("<html></html");

/* Results in <![CDATA[<html></html]]> */

/* Create DOMProcessingInstruction */

$pi = $dom->createProcessingInstruction("php", "echo 'Hello World';");

$pi = new DOMProcessingInstruction("php", "echo 'Hello World';");

/* Results in <?php echo 'Hello World';?> */

/* Create DOMEntityReference */

$entityref = $dom->createEntityReference("lt");

$entityref = new DOMEntityReference("lt");

/* Results in < */

$frag = $dom->createDocumentFragment();

$frag->appendChild(new DOMElement("node1", "node1 value"));

$frag->appendChild(new DOMElement("node2", "node2 value"));

$frag = $dom->createDocumentFragment();

$frag->appendXML("<node1>node1 value</node1><node2>node2 value</node2>");

移除和替代节点

之前已经有一个方法setAttributeNode()可以用来移除和替代节点,如果有一个旧的节点跟新的节点同名的话, setAttributeNode()将移除旧的节点并以新的节点替代它,这只在属性类型的节点上奏效。对于其它节点可以使用replaceChild ()和removeChild()方法来实现。

首先创建一个文档:

$doc = DOMDocument::loadXML('<?xml version="1.0"?>

<root>

<child1>child1 content</child1>

<child2>child2 content</child2>

<child3>child3 content</child3>

</root>');

接着要把child2元素删除,并把child3元素用newchild元素替代。第一步是取得这些元素。

$root = $doc->documentElement;

$child2 = $root->getElementsByTagName("child2")->item(0);

$child3 = $root->getElementsByTagName("child3")->item(0);

接着删除$child2对象:

$root->removeChild($child2);

这时候XML文档将会变成如下的结构:

<?xml version="1.0"?>

<root>

<child1>child1 content</child1>

<child3>child3 content</child3>

</root>

在文档中会留出原来元素所占用的行。接着创建一个newchild元素来替代child3。

$oldchild = $root->replaceChild(new DOMElement("newchild", "new content"), $child3);

这样,XML文档变成如下结构:

<?xml version="1.0"?>

<root>

<child1>child1 content</child1>

<newchild>new content</newchild>

</root>

那一行空白也是一个节点,可以用下面的代码删除:

$children = $root->childNodes;

for ($x=$children->length; $x--; $x>=0) {

$node = $children->item($x);

if ($node->nodeType == XML_TEXT_NODE && $node-> isElementContentWhitespace()) {

$root->removeChild($node);

}

}

使用XPath

从PHP5.0开始,可以使用DOMXPath对象的query()方法返回一个包含节点的DOMNodeList对象。PHP5.1开始提供了一个evaluate()方法,它可以返回更多类型的节点对象。

实例化DOMXPath

使用new关键字创建一个DOMXPath对象,并传入一个DOMDocument对象类型的参数。

$domxpath = new DOMXPath($dom);

使用query()方法

PHP5的所有版本都有query()方法,可以利用这个方法和XPath表达式从树形结构获取一些节点,以DOMNodeList对象的形式返回,如果没有获取到任何一个节点就返回一个空的DOMNodeList。可以将一个XPath表达式作为一个参数传给query()方法,比如:

$list = $domxpath->query("/book/bookinfo/author");

$author = $list->item(0);

$list = $domxpath->query("surname", $author);

$surname = $list->item(0);

如果你试图利用XPath表达式返回一个字符串,你会发现返回的是一个空的DOMNodeList对象:

$list = $domxpath->query("string('/book/bookinfo/author/surname')");

var_dump($list);

print "Number of Nodes Returned: ".$list->length."/n";

length返回0。

使用evaluate()方法

PHP5.1开始增加了evaluate()方法,可以像query()方法一样使用它。

$list = $domxpath->evaluate("string(/book/bookinfo/author/surname)");

var_dump($list);

var_dump()函数输出string(8) “Richards”。说明evaluate()方法实现了query()所不能实现的功能。

evaluate()方法返回值的类型很多,有Boolean,Integer,String,Null和DOMNodeList,返回什么类型的值取决于使用你所使用的XPath表达式,如:

$newyear = $domxpath->evaluate("number(/book/bookinfo/copyright/year) + 1");

var_dump($newyear);

像query()方法一样,evaluate()可以返回一个包含多个节点的DOMNodeList对象,如:

$list = $domxpath->evaluate("/book/bookinfo/author");

$author = $list->item(0);

print $author->nodeName."/n";

来源:http://blog.csdn.net/guoguo1980/archive/2008/05/12/2436299.aspx
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: