轉載地址 http://www.yebihai.com/web/xml/292.html
SimpleXML使用詳解
介紹
SimpleXML提供了一種簡單,直觀的方法來處理XML。它只有一個單一類型的類,三個函數和六個方法。
使用SimpleXML
SimpleXMLElement 類是這個擴展中所有操作的核心類。可以用new關鍵字直接創建這種類,或是使用simplexml_load_file()或 simplexml_load_string()函數返回這種類。本文將使用清單7-1的XML文檔來說明如何使用SimpleXML,將此文檔命名爲 sml.xml。
清單7-1 sml.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 lang="en">
<bookinfo>
<title>SimpleXML in PHP 5</title>
<author>
<firstname>Rob</firstname>
<surname>Richards</surname>
</author>
<copyright>
<year>2005</year>
<holder>Rob Richards</holder>
</copyright>
</bookinfo>
<preface>
<title>Using SimpleXML</title>
<para>An example DOM Tree using DocBook.</para>
</preface>
<chapter id="navigation">
<title>Accessing Elements</title>
<para>Elements are accessed as properties</para>
<para>
<![CDATA[
<?php
$data = '<?xml version="1.0"?>
<root>content</root>';
$sxe = simplexml_load_string($data);
var_dump($sxe);
?>
]]>
</para>
</chapter>
</book>
創建一個SimpleXMLElement對象
使用new關鍵字創建
$xml = "<root><node1>Content</node1></root>";
$sxe = new SimpleXMLElement($xml);//SimpleXMLElement Object ( [node1] => Content )
使用simplexml_load_string()創建
$xml = "<root><node1>Content</node1></root>";
$sxe = simplexml_load_string($xml);//SimpleXMLElement Object ( [node1] => Content )
如何選擇這兩種創建SimpleXMLElement的方法呢?simplexml_load_string()提供了更多的函數,比如控制解析選項的能力。如果不需要這些額外的函數的話就可以憑個人愛好選擇一種方法。
使用simplexml_load_file()從一個URI創建
$sxe = simplexml_load_file("filename.xml");
simplexml_load_string()和simplexml_load_file()都有一個必需的參數和可選的參數。從PHP5.1開始simplexml_load_file()多了一個用來控制解析行爲的第三個參數。
/* Prototype for PHP 5.0 */
simplexml_load_file(string data [, string class_name])
/* Prototype for PHP 5.1 */
simplexml_load_file(string data [, string class_name [, int options]])
保存XML數據
與DOM擴展一樣,SimpleXML也提供了一個用來輸出XML內容的方法asXML()。可以用這個方法以字符串或文件形式輸出這個文檔或文檔中的某個節點。
$xml = "<root><node1>content</node1></root>";
$sxe = new SimpleXMLElement($xml);
print $sxe->asXML();
$sxe->asXML('test.xml');
輸出:
<?xml version="1.0"?>
<root><node1>content</node1></root>
訪問元素節點
在SimpleXML中,可以直接通過元素的名稱來訪問特定的元素。
訪問元素
當一個文檔被載入SimpleXML時,文檔被看成是一個SimpleXML對象,文檔中的所有元素都被看成是該對象的屬性。
<?php
$book = simplexml_load_file('sxml.xml');
/* Access the bookinfo child element of the book element */
$bookinfo = $book->bookinfo;
/* Access the title child element from the bookinfo element */
$title = $bookinfo->title;
?>
如果使用DOM來訪問title,代碼如下
$dom = new DOMDocument();
$dom->load('sxml.xml');
$book = $dom->documentElement;
foreach($book->childNodes as $node) {
if ($node->nodeName == "bookinfo") {
foreach($node->childNodes as $child) {
if ($child->nodeName == "title") {
$node = $child;
break 2;
}
}
}
}
if ($node) {
$title = $node;
}
顯然SimpleXML對的起它的名字。
訪問內容
<?php
$book = simplexml_load_file('sxml.xml');
$bookinfo = $book->bookinfo;
$title = $bookinfo->title;
/* Object examined with var_dump */
var_dump($title);
/* Using print with element containing text-only content */
print "Title: ".$title."\n";
$author = $bookinfo->author;
/* Object examined with var_dump */
var_dump($author);
/* Using print with element containing child elements */
print "Author: ".$author."\n";
?>
這段代碼檢查了兩個SimpleXMLElement對象,$author和$title。兩者的區別是$author元素有包含子元素而$title元素只包含一個文本節點。
輸出如下:
object(SimpleXMLElement)#4 (1) {
[0]=>
string(18) “SimpleXML in PHP 5″
}
Title: SimpleXML in PHP 5
object(SimpleXMLElement)#6 (2) {
[”firstname”]=>
string(3) “Rob”
[”surname”]=>
string(8) “Richards”
}
Author:
檢查輸出結果可以發現,$title是一個包含有文本內容的SimpleXMLElement對象,索引0表示元素的文本內容,當打印$title時,文本內容將以字符串形式返回。
$author元素有兩個子元素,從輸出結果可以看出,這些子元素被看成SimpleXMLElement對象的屬性,這些屬性的值是它們對應的節點包含的內容。用print輸出$author時,輸出結果是空格和換行符。
如果一個元素無子元素,只包含文本內容,那麼可以將此元素所對應的SimpleXMLElement對象視爲一個字符串來使用,有些情況下,爲了獲得以字符串形式返回文本內容,必須執行類型轉換操作:
$titlecontent = (string) $title;
有子元素的元素所對應的SimpleXMLElement對象將返回該對象直屬的文本節點,而不是任一子元素的內容。如果用print輸出$author,將得到一個27字符長度,包含空格和換行符的字符。 Neither of the child elements,firstname or surname, nor their content is returned in the string.
爲了理解最後一點,可以運行一些下面這段代碼:
$doc = new SimpleXMLElement('<root>some<child1>subtext</child1>thing</root>');
print $doc;
使用迭代對象
SimpleXMLElement 對象在大多數情況下是可迭代的,可以用這個特性來訪問文檔中多個元素名一樣的節點,如清單7-1中的para元素。在使用元素名作爲屬性來訪問一個元素時,SimpleXMLElement對象不是一個單一節點的存取器,它實際上是作爲屬性訪問的元素名節點的集合。直接利用元素名作爲屬性訪問實際上訪問訪問這個集合中的第一個元素。
看下這段代碼
$book = simplexml_load_file('sxml.xml');
$para = $book->chapter->para;
print $para."\n";
foreach($para AS $node) {
print $node."\n";
}
這段代碼中,$para變量就是para元素的集合,實際上包含了兩個元素。如果直接訪問$para的話實際上是訪問第一個para元素,利用迭代可以看出 $para所包含的內容是兩個元素。其中CDATA節點被看做是純文本的內容,其中包含的空格和換行符都會被如實輸出。
用迭代的方式來訪問所有的元素顯然不太實際,有時候我們希望訪問結果集中的某個特定的元素,這時可以使用從0開始的索引來訪問這個結果集,例如:
$book = simplexml_load_file('sxml.xml');
$para = $book->chapter->para[1];
print "Content: ".$para."\n";
foreach($para AS $node) {
print "Iter Content: ".$node."\n";
}
運行這段代碼會發現foreach循環失效了,這是因爲SimpleXML知道你只是在尋找結果集中某個特定的元素,這種情況下對象是不可迭代的。
Caution:使用索引方式訪問一個SimpleXMLElement對象會返回一個不可以迭代的對象,因爲它是一個單一的元素而不是一個元素集。
訪問未知元素
在不知道XML文檔的結構的情況下可以利用SimpleXML中的children()方法來返回一個可以迭代方式訪問的某個元素的所有子元素的SimpleXML對象。如:
$book = simplexml_load_file('sxml.xml');
$author = $book->bookinfo->author;
$children = $author->children();
foreach($children AS $child) {
print $child."\n";
}
上述代碼用children()方法返回了author節點下的所有子元素,然後用foreach循環輸出。也可以使用索引方式訪問返回的子元素,如echo $children[1];。
理解PHP對象函數
SimpleXMLElement對象的屬性是動態的,因爲這些屬性是由對象實例決定的,而不是由類本身決定的。在PHP中,可以利用get_object_vars()函數來返回某個對象的所有屬性,返回的結果是一個包含屬性和值的數組,如:
$props = get_object_vars($author);
foreach ($props AS $name=>$value) {
print $name.": ".$value."\n";
}
輸出:
firstname: Rob
surname: Richards
這段代碼訪問的子元素都只包含文本內容,所以返回的數組只包含屬性名和值,對於一個包含許多子節點的元素,返回的結果稍微複雜點:
$props = get_object_vars($book->bookinfo);
var_dump($props);
輸出:
array(3) {
[”title”]=>
string(18) “SimpleXML in PHP 5″
[”author”]=>
object(SimpleXMLElement)#4 (2) {
[”firstname”]=>
string(3) “Rob”
[”surname”]=>
string(8) “Richards”
}
[”copyright”]=>
object(SimpleXMLElement)#5 (2) {
[”year”]=>
string(4) “2005″
[”holder”]=>
string(12) “Rob Richards”
}
}
使用DOM互操作
另一種訪問未知元素的方法是使用DOM,可以將一個節點導入DOM擴展,然後使用DOM的屬性和方法來處理。
$book = simplexml_load_file('sxml.xml');
$author = $book->bookinfo->author;
$children = $author->children();
foreach($children AS $child) {
/* Import node into DOM, and get nodeName */
$element = dom_import_simplexml($child);
$name = $element->nodeName;
print $name.": ".$child."\n";
}
將節點導入DOM擴展時並沒有創建一個節點的副本(copy),而是直接訪問導入的節點(JIMMY注:這個概念很重要)。
修改內容
利用SimpleXML修改元素內容非常方便,你可以改變或移除樹中的某個元素,但是不能直接在樹中添加一個元素。要添加一個元素,可以使用DOM的互操作性:
$xml = "<root><node1>content</node1></root>";
$sxe = new SimpleXMLElement($xml);
$dom = dom_import_simplexml($sxe);
$dom->appendChild(new DOMElement("node2", "content2"));
print $sxe->asXML();
輸出:
<?xml version="1.0"?>
<root><node1>content</node1><node2>content2</node2></root>
編輯文本內容
可以利用SimpleXML的屬性賦值方法來直接編輯一個元素的內容,要主意的是如果文檔中有多個元素名一樣的元素,如果沒有使用索引來指定要編輯哪個元素時PHP將發出一個警告。如:
$book = simplexml_load_file('sxml.xml');
/* Modify an unspecified para element where multiple para elements exist */
$book->chapter->para = "Removed CDATA";
輸出:
Warning: main() [/phpmanual/function.main.html]: Cannot assign to an array of nodes
(duplicate subnodes or attr detected)
必須給para指定索引告訴程序你要編輯哪個元素:
$book = simplexml_load_file('sxml.xml');
$book->chapter->para[1] = "Removed CDATA";
print $book->chapter->asXML();
輸出:
<chapter id="navigation">
<title>Acessing Elements</title>
<para>Elements are accessed as properties</para>
<para>Removed CDATA</para>
</chapter>
這樣,第二個para元素的內容被改爲Removed CDATA。如果要編輯一個在文檔中唯一存在的元素可不必指定索引,直接修改。如修改title:
$book = simplexml_load_file('sxml.xml');
$book->chapter->title = "New Title";
$book->chapter->para[1] = "Removed CDATA";
print $book->chapter->asXML();
輸出:
<chapter id="navigation">
<title>New Title</title>
<para>Elements are accessed as properties</para>
<para>Removed CDATA</para>
</chapter>
強烈建議使用索引來編輯元素,除非你對文檔的結果非常確定。使用索引來編輯title元素會比較安全,如$book->chapter->title[0] = “New Title”;這行代碼用索引[0]指定要編輯第一個title。
編輯有子樹的元素
$book = simplexml_load_file('sxml.xml');
$cholder = $book->bookinfo->copyright->holder;
print $cholder->asXML()."\n";
$book->bookinfo = "No Book Info";
print $book->bookinfo->asXML()."\n";
print $cholder->asXML()."\n";
輸出:
<holder>Rob Richards</holder>
<bookinfo>No Book Info</bookinfo>
Warning: SimpleXMLElement::asXML() [/phpmanual/function.asXML.html]: Node no
longer exists in N:\CVS Projects\php5\Debug_TS\booksxe.php on line 7
這段代碼中,首先將文檔中的holder元素賦值給$cholder變量,然後打印該變量。bookinfo元素包含有title,author和 copyright子樹,它的內容被字符串No Book Info代替,從bookinfo的輸出結果可以看出,它的子樹被清空並且被字符串代替了。接着試圖再次打印$cholder變量的XML內容,程序輸出一個警告,這個變量依然是一個SimpleXMLElement對象,但它所屬的節點在bookinfo元素改變時已經被破壞了。
另一種情況。將bookinfo元素中的子元素用字符串 代替。
$book = simplexml_load_file('sxml.xml');
$book->bookinfo = "<title>SimpleXML in PHP 5</title>";
print $book->bookinfo->asXML()."\n";
如果你認爲上述代碼將bookinfo中的內容清空後再給bookinfo創建了一個子節點title,那麼你錯了。輸出結果是
<title>SimpleXML in PHP 5</title>
實際上bookinfo元素的子元素都被移除了,但是新賦值的XML數據被轉義成文本內容,而不是一個新的子元素。
如果想用一個子樹代替另一個子樹,可以利用DOM擴展:
$book = simplexml_load_file('sxml.xml');
$bookinfo = dom_import_simplexml($book->bookinfo);
/* 移除bookinfo元素下的所有子元素*/
while ($bookinfo->firstChild) {
$bookinfo->removeChild($bookinfo->firstChild);
}
$bookinfo->appendChild(new DOMElement("title", "SimpleXML in PHP 5"));
print $book->bookinfo->asXML()."\n";
輸出結果:
<bookinfo>
<title>SimpleXML in PHP 5</title>
</bookinfo>
移除元素
可以用PHP內置函數unset()來將一個元素從樹中移除。unset()的參數必須是一個SimpleXMLElement,用屬性方法來訪問要移除的元素。例如,從chapter節點移除title元素:
$book = simplexml_load_file('sxml.xml');
$book->chapter->para[1] = "Removed CDATA";
unset($book->chapter->title);
print $book->chapter->asXML();
上述代碼執行後,chapter的結構爲:
<chapter id="navigation">
<para>Elements are accessed as properties</para>
<para>Removed CDATA</para>
</chapter>
將這個結果與下面代碼執行的結果想比較:
$book = simplexml_load_file('sxml.xml');
$book->chapter->para[1] = "Removed CDATA";
$title = $book->chapter->title;
unset($title);
print $book->chapter->asXML();
輸出結果
<chapter id="navigation">
<title>Acessing Elements</title>
<para>Elements are accessed as properties</para>
<para>Removed CDATA</para>
</chapter>
title元素沒有被移除,unset函數只對$title變量作用並沒有將title元素從樹中移除。
在移除一個元素時必須注意,用索引來指定的特定元素不會被移除:
$book = simplexml_load_file('sxml.xml');
$book->chapter->para[1] = "Removed CDATA";
unset($book->chapter->title[0]);
print $book->chapter->asXML();
輸出:
<chapter id="navigation">
<title>Acessing Elements</title>
<para>Elements are accessed as properties</para>
<para>Removed CDATA</para>
</chapter>
如果要移除所有的para元素時可以利用下面代碼:
$book = simplexml_load_file('sxml.xml');
unset($book->chapter->para);
print $book->chapter->asXML();
輸出:
<chapter id="navigation">
<title>Acessing Elements</title>
</chapter>
問題是如果你只想移除其中的一個para元素時要怎麼辦。這時可以再次用到DOM擴展:
$book = simplexml_load_file('sxml.xml');
$chapter = dom_import_simplexml($book->chapter);
$node = $chapter->lastChild;
while($node) {
if ($node->nodeName == "para") {
$chapter->removeChild($node);
$node = NULL;
break;
}
$node = $node->previousSibling;
}
print $book->chapter->asXML();
輸出:
<chapter id="navigation">
<title>Acessing Elements</title>
<para>Elements are accessed as properties</para>
</chapter>
所幸的是PHP5.2開始已經支持刪除用索引指定的元素了:
$book = simplexml_load_file('sxml.xml');
unset($book->chapter->para[1]);
print $book->chapter->asXML();
訪問屬性
讀取屬性
下面的代碼輸出了book元素中的lang屬性
$book = simplexml_load_file('sxml.xml');
print $book['lang'];
訪問用索引指定的元素的屬性:
$book = simplexml_load_file('sxml.xml');
print $book->chapter[0]['id'];
在不知道屬性名的情況下可以用attributes()方法來輸出屬性:
$book = simplexml_load_file('sxml.xml');
foreach($book->chapter->attributes() AS $attribute) {
print $attribute."\n";
}
如果要獲得位置的屬性名,可以使用DOM擴展:
$book = simplexml_load_file('sxml.xml');
foreach($book->chapter->attributes() AS $attribute) {
$att = dom_import_simplexml($attribute);
print $att->nodeName."\n";
print $attribute."\n";
}
修改屬性
修改屬性的值跟修改元素的值一樣,直接對其賦值就可以了:
$book = simplexml_load_file('sxml.xml');
$book['lang'] = "es";
print $book['lang'];
添加一個屬性也很簡單,如果對一個不存在的屬性名進行賦值就給元素創建一個新屬性
$book = simplexml_load_file('sxml.xml');
$book->bookinfo->author->firstname["prefix"] = "Mr.";
print $book->bookinfo->author->asXML();
輸出:
<author>
<firstname prefix="Mr.">Rob</firstname>
<surname>Richards</surname>
</author>
移除屬性
移除屬性也用到unset()函數:
$book = simplexml_load_file('sxml.xml');
$book->bookinfo->author->firstname["prefix"] = "Mr.";
print $book->bookinfo->author->firstname->asXML()."\n\n";
unset($book->bookinfo->author->firstname["prefix"]);
print $book->bookinfo->author->firstname->asXML();
輸出:
<firstname prefix="Mr.">Rob</firstname>
<firstname>Rob</firstname>
擴展SimpleXMLElement類
class mySXE extends SimpleXMLElement {
function appendChild($name, $content) {
$dom = dom_import_simplexml($this);
$dom->appendChild($dom->ownerDocument->createElement($name, $content));
}
}
當實例化擴展的類時,文檔的每個節點對象的類型都是擴展類的類型。
$sxe = new mySXE("<root><node1></node1></root>");
$sxe->node1->appendChild("node2", "content");
print $sxe->asXML();
輸出:
<?xml version="1.0"?>
<root><node1><node2>content</node2></node1></root>
使用new方法可以用來處理字符串類型的XML,如果XML保存在一個文件中,那麼可以將擴展的類名作爲第二個參數傳給simplexml_load_string或simplexml_load_file
$sxe = simplexml_load_string("<root><node1></node1></root>", "mySXE");
$sxe->node1->appendChild("node2", "content");
print $sxe->asXML();
輸出結果與用new關鍵字輸出的結果一樣。
在SimpleXML中使用命名空間
將清單7-1的內容改爲
<?xml version="1.0" encoding="UTF-8"?>
<book lang="en">
<bookinfo xmlns="http://www.example.com/ns1">
<title>SimpleXML in PHP 5</title>
<author>
<firstname>Rob</firstname>
<surname>Richards</surname>
</author>
<copyright>
<year>2005</year>
<holder>Rob Richards</holder>
</copyright>
</bookinfo>
</book>
如果試圖用普通的方法來訪問元素或屬性,你會分析這並不可行,例如:
$book = simplexml_load_file('sxmlns.xml');
print $book["lang"]."\n";
print $book->bookinfo->title."\n";
輸出的結果是兩行空白。
在訪問命名空間節點前,必須使用children()和attributes()方法。這兩個方法不僅可以在沒有指定參數的時候使用,也可以在指定一個 URI命名空間作爲參數使用。如果一個SimleXMLElement對象是從這兩個方法返回的,那麼你就可以像普通的元素和屬性一樣訪問命名空間下的元素和屬性:
$book = simplexml_load_file('sxmlns.xml');
/* 返回 http://www.example.com/ns2 命名空間下的所有屬性 */
$bookatts = $book->attributes("http://www.example.com/ns2");
print $bookatts["lang"]."\n";
/* 返回 http://www.example.com/ns1 命名空間下的所有元素*/
$bookns = $book->children("http://www.example.com/ns1");
$bookinfo = $bookns->bookinfo;
/* 重置命名空間來訪問非命名空間的元素 */
$nonsbkinfo = $bookinfo->children();
print $nonsbkinfo->title."\n";
children ()和attributes()方法可以被看做是過濾器,如果沒有參數或者傳遞一個NULL作爲參數,這兩個方法將返回非命名空間下的元素或屬性;否則將返回特定命名空間下的元素或屬性。在重置之前,命名空間仍然起作用並且被子節點繼承。例如,使用$bookinfo被設置爲命名空間 http://www.example.com/ns1的對象,可以使用 print $bookinfo->author->firstname來但因author中的firstname元素。所有的元素都在命名空間下,因此你在創建$bookinfo對象時不必一直使用children()設置命名空間。
本文來源:http://hi.baidu.com/bailu1234/blog/item/c88fd1c2e41fa358b319a893.html