使用php的SimpleXML快速處理XML文件
瞭解和 PHP 版本 5 捆綁到一起的 SimpleXML 擴展,它使 PHP 頁面能夠以 PHP 友好的語法來查詢、搜索、修改和重新發佈 XML。
PHP 版本 5 引入了 SimpleXML,一種用於讀寫 XML 的新的應用程序編程接口(API)。在 SimpleXML 中,下面的這樣的表達式:
$doc->rss->channel->item->title |
從文檔中選擇元素。只要熟悉文檔的結構,很容易編寫這種表達式。但是,如果不很清楚需要的元素出現在何處(比如 Docbook、HTML 和類似的敘述性文檔中),SimpleXML 可以使用 XPath 表達式尋找這些元素。
開始使用 SimpleXML
假設需要一個 PHP 頁面將 RSS 提要(feed)轉化成 HTML。RSS 是一種簡單的 XML
格式用於發佈連鎖內容。文檔的根元素是 rss
,它包括一個 channel
元素。channel
元素包含關於提要的元數據,如標題、語言和 URL。它還包含各種封裝在
item
元素中的報導。每個 item
都有一個
link
元素,包括一個 URL,還有 title
或
description
(通常兩者都有),包含普通文本。不使用名稱空間。RSS
的內容當然不止這些,不過對本文來說知道這些就足夠了。清單
1 顯示了一個典型的例子,它包含兩個新聞項。
清單 1. RSS 提要
|
我們來開發一個 PHP 頁面將 RSS 提要格式化為 HTML。清單 2 顯示了這個頁面的基本結構。
清單 2. PHP 代碼的靜態結構
|
第一步是解析 XML 文檔並保存到變量中。只需要一行代碼,向 simplexml_load_file()
函數傳遞一個 URL 即可:
$rss = simplexml_load_file('http://partners.userland.com/nytRss/nytHomepage.xml'); |
|
對於這個例子,我已經從 Userland 的 New York Times 提要(在 http://partners.userland.com/nytRss/nytHomepage.xml)填充了頁面。當然,也可使用其他 RSS 提要的任何 URL。
要注意,雖然名稱為 simplexml_load_file()
,該函數實際上解析遠程
HTTP URL 上的 XML 文檔。但這並不是該函數唯一令人感到奇怪的地方。返回值(這裡存儲在
$rss
變量中)並沒有指向整個文檔,如果使用過其他 API
如文檔對象模型(DOM)您可能會這樣期望。相反,它指向文檔的根元素。從 SimpleXML 不能訪問文檔序言和結語部分的內容。
整個提要的標題(不是提要中各報導的標題)位於 rss
根元素 channel
的 title
孩子中。很容易找到這個標題,就彷彿 XML 文檔是類 rss
的一個對象的序列化形式,它的 channel 字段本身帶有一個
title 字段。使用常規 PHP 對象引用語法,尋找標題的語句如下:
$title = $rss->channel->title; |
找到之後可以將其添加到輸出 HTML 中。這樣做很簡單,只要回顯 $title
變量即可:
<title><?php echo $title; ?></title> |
這一行輸出元素的字符串值而不是整個元素。就是說寫入文本內容但不包括標籤。
甚至可以完全跳過中間變量 $title
:
<title><?php echo $rss->channel->title; ?></title> |
因為該頁面在多處重用這個值,我發現用一個含義明確的變量來存儲會更方便。
然後必須發現提要中的項。完成這項任務的表達式很簡單:
$rss->channel->item |
但是,提要通常包含多個新聞項。但也可能一個也沒有。因此,該語句返回一個數組,可以通過 for-each
循環來遍歷它:
foreach ($rss->channel->item as $item) { |
通過從 RSS 提要中讀取 link
元素值添加鏈接也很容易。只要在 PHP 中輸出一個
a
元素,並使用 $item->link
檢索 URL
即可。清單
3 增加了該元素並填充到
清單 1 的框架中。
清單 3. 簡單而完整的 PHP RSS 閱讀器
</h1> |
|
錯誤處理
並非所有 RSS 提要都如期望的那樣結構良好。XML
規範要求處理程序在發現結構良好性錯誤時停止處理文檔,SimpleXML 是符合標準的 XML
處理程序。但是在發現錯誤時它沒有提供多少幫助。一般來說,它在 php-errors
文件中記錄錯誤(但是不包括詳細的錯誤消息),simplexml-load-file()
函數返回 FALSE。如果不能確保解析的文件是結構良好的,在使用文件數據之前要檢查錯誤,如清單
4 所示。
清單 4. 避免結構錯誤的輸入
|
其他常見的錯誤是文檔實際上是結構良好的,但是沒有在期望的地方包含期望的元素。如果項沒有標題(比如在
top-100 這樣的 RSS
提要中),$doc->rss->channel->item->title
這樣的表達式會怎麼樣呢?最簡單的辦法是將返回值永遠看作一個數組並循環遍歷該數組。這樣就可以判斷元素比預期的多還是少。但是,如果確定只需要文檔中的第一個元素
—— 即使有多個,可以按索引訪問,索引號從零開始。比如,如果要請求一個項的標題,可以用如下代碼:
$doc->rss->channel->item[0]->title[0] |
如果沒有第一項,或者第一項沒有標題,該項就按照常規 PHP 數組索引越界處理。即結果是空,在將其插入輸出 HTML 時,它會被轉化成空白字符串。
識別和拒絕不打算處理的非預期格式通常屬於 XML 驗證解析器的範疇。然而,SimpleXML 不能針對文檔類型定義(DTD)或模式進行驗證。它只檢查結構良好性。
處理名稱空間
很多站點現在從 RSS 轉向了 Atom。清單
5 顯示了一個 Atom 文檔的例子。該文檔大部分和 RSS
的例子類似。但是增加了一些元數據,而且根元素變成了 feed
而不是
rss
。feed
元素包含 entry
而不是項(item)。content
元素代替了
description
。最重要的是,Atom 文檔使用了名稱空間,但 RSS
文檔沒有。這樣,Atom 文檔就可以在內容中內嵌真正的、沒有轉義的可擴展 HTML(XHTML)。
清單 5. Atom 文檔
|
雖然元素名稱變了,但用 SimpleXML 處理 Atom 文檔的基本方法和 RSS
相同。一個區別是現在請求被命名的元素和本地名稱時必須指定名稱空間統一資源標識符(URI)。這需要兩個步驟:首先通過向
children()
函數傳遞名稱空間 URI
請求給定名稱空間中的孩子元素。然後用那個名稱空間中適當的本地名稱請求元素。假設第一次把 Atom
提要加載到變量 $feed
中,如下所示:
$feed = simplexml_load_file('http://www.cafeconleche.org/today.atom'); |
下面的兩行尋找 title
元素:
$children = $feed->children('http://www.w3.org/2005/Atom'); |
如果願意可以將這些代碼壓縮成一行,雖然行會變得有點長。名稱空間中的所有其他元素也必須類似處理。清單 6 給出了一個完整的 PHP 頁面,其中顯示帶名稱空間的 Atom 提要中的標題。
清單 6. 簡單的 PHP Atom 標題閱讀器
</h1> |
echo "<p>" . $details->content->asXML() . "</p>"; |
生成的結果如清單 7 所示。
清單 7. asXML 輸出
|
這不是純粹的 XHTML。content
元素悄悄從 Atom
文檔中溜了進來,您真的不願這樣。更糟的是,它的名稱空間不對,因此不能被識別。幸運的是,這個多出來的元素實際上沒有多大害處,因為
Web
瀏覽器會忽略不認識的任何標籤。完成的文檔是無效的,但是關係不大。如果還是覺得彆扭,可以通過字符串操作將其去掉,如下所示:
$description = $details->content->asXML(); |
為了使代碼更加健壯,可以使用正則表達式而不是假定起始標籤和前面相同。具體來說,可以考慮各種可能的屬性:
// end-tag is fixed in form so it's easy to replace |
即使這樣改進之後,代碼還是會在註釋、處理指令和 CDATA 節上出錯。無論怎麼分解,恐怕都不會簡單了。混合內容實際上超出了 SimpleXML 所能處理的範圍。
XPath
只要知道文檔有什麼元素以及在什麼位置,$rss->channel->item->title
這樣的表達式很方便。但是,不一定會知道得這麼清楚。比方說,在 XHTML
中,標題元素(h1
、h2
、h3
等等)可以是
body
、div
、table
或其他幾種元素的孩子。此外,div
、table
、blockquote
及其他元素又可以互相嵌套多次。在很多不那麼明確的場合中,使用
//h1
或
//h1[contains('Ben')]
這樣的 XPath
表達式更方便。SimpleXML 通過 xpath()
函數支持這種功能。
清單 8 顯示的 PHP 頁面列出了 RSS 文檔中的所有標題,包括提要本身以及每個項的標題。
清單 8. 使用 XPath 查找 title 元素
|
SimpleXML 僅支持 XPath 位置路徑及位置路徑的組合。不支持那些不返回節點集的
XPath 表達式,如 count(//para)
或
contains(title)
。
從 PHP 5.1 版開始,SimpleXML 可以直接對帶名稱空間的文檔使用 XPath
查詢。和通常一樣,XPath
位置路徑必須使用名稱空間前綴,即使搜索的文檔使用默認名稱空間也仍然如此。registerXPathNamespace()
函數把前綴和後續查詢中使用的名稱空間 URL 聯繫在一起。比方說,如果要查詢 Atom
文檔中的所有 title
元素,應使用清單
9 中所示的代碼。
清單 9. 使用 XPath 和名稱空間
|
最後一點忠告:PHP 中的 XPath 速度非常慢。當改為 XPath 表達式之後頁面加載延遲從難以覺察變成了幾秒鐘,即使是在負荷不高的本地服務器上。如果採用這些技術,必須使用某種緩存技術來獲得適當的性能。不可能動態生成每個頁面。
|
結束語
如 果不需要處理混合內容,SimpleXML 對於 PHP 程序員的工具箱來說是個不錯新玩意。其適用的情況很多。具體而言,它能夠很好地處理簡單的、類似記錄的數據。只要文檔層次不深、不很複雜,而且沒有混合內 容,SimpleXML 要比使用 DOM 簡單得多。如果事先知道文檔結構該工具將更有用,雖然通過 XPath 可以滿足這種要求。雖然不支持驗證和混合內容有點不方便,但不是絕對的。很多簡單格式沒有混合內容,而且很多應用只涉及到可預知的數據格式。如果符合您的 需要,可以自己嘗試一下 SimpleXML。只要對錯誤處理稍加注意,並且通過緩存來解決性能問題,SimpleXML 可以成為 PHP 中一種可靠、健壯的 XML 處理方法。
參考資料
學習- 您可以參閱本文在 developerWorks 全球網站上的 英文原文。
-
SimpleXML
的官方文檔:仔細閱讀 PHP 版本 5 用戶手冊,瞭解這個將 XML
轉換成對象以便用一般屬性選擇器和數組迭代器處理的工具集。
-
XML
in a Nutshell(Elliotte Rusty
Harold 和 W. Scott
Means,O'Reilly,2005):該書集中介紹了 XML,請深入閱讀關於
XML 和 XPath 的內容。
- 「Ajax
RSS reader」(Jack D.
Herrington,developerWorks,2006 年 5 月):討論了在
PHP 中使用數據庫、JavaScript 代碼以及 DOM 編寫 RSS
的一種高級方法。
- 「An
overview of the Atom version 1.0 Syndication
Format」(James Snell,developerWorks,2005 年
8 月):討論了 Atom
與其他聯合格式相比在技術上的優勢,並通過幾個典型的例子加以說明。
-
IBM XML 認證:看看如何才能成為一名 IBM 認證的 XML
及相關技術的開發人員。
-
XML 技術庫:developerWorks XML
專區提供了大量技術文章和技巧、教程、標準以及 IBM 紅皮書。
- 隨時關注developerWorks
技術事件和網絡廣播。
獲得產品和技術
- 使用IBM
試用軟件:構建您的下一個項目,可直接從 developerWorks
下載。
討論
-
XML 專區討論論壇(英文):參與任何一個面向 XML 的論壇。
- developerWorks blogs:加入 developerWorks 社區。
|
Elliotte Rusty Harold 來自新奧爾良, 現在他還定期回老家喝一碗美味的秋葵湯。不過目前,他和妻子 Beth 定居在紐約臨近布魯克林的 Prospect Heights,同住的還有他的貓咪 Charm(取自夸克)和 Marjorie(取自他岳母的名字)。他是 Polytechnic 大學計算機科學的副教授,他在該校講授 Java 和面向對象編程。他的 Web 站點 Cafe au Lait 已經成為 Internet 上最流行的獨立 Java 站點之一,它的姊妹站點 Cafe con Leche 已經成為最流行的 XML 站點之一。他的書包括 Effective XML、 Processing XML with Java、 Java Network Programming 和 The XML 1.1 Bible。他目前在從事處理 XML 的 XOM API、Jaxen XPath 引擎和 Jester 測試覆蓋率工具的開發工作。 |