雖然說現在很多的服務提供者都會提供JSON 介面供我們使用,但是,還是有不少的服務依然必須使用XML 作為介面格式,這就需要我們來對XML 格式的數據進行解析轉換。而 PHP 中並沒有像 json_encode() 、 json_decode() 這樣的函數能夠讓我們方便地進行轉換,所以在操作 XML 資料時,大家往往都需要自己寫程式來實現。
今天,我們介紹的是使用 SPL 擴充庫中的一些物件方法來處理 XML 資料格式的轉換。首先,我們定義一個類,就相當於封裝一個操作 XML 資料轉換的類,方便我們將來使用。如果只是測試效果的話,直接寫下面的函數也是可以的。
class ConvertXml{ // .... }
XML 轉換為 PHP 陣列
class ConvertXml{ public function xmlToArray(SimpleXMLIterator $xml): array { $res = []; for ($xml->rewind(); $xml->valid(); $xml->next()) { $a = []; if (!array_key_exists($xml->key(), $a)) { $a[$xml->key()] = []; } if ($xml->hasChildren()) { $a[$xml->key()][] = $this->xmlToArray($xml->current()); } else { $a[$xml->key()] = (array) $xml->current()->attributes(); $a[$xml->key()]['value'] = strval($xml->current()); } $res[] = $a; } return $res; } // ..... } $wsdl = 'http://flash.weather.com.cn/wmaps/xml/china.xml'; $xml = new SimpleXMLIterator($wsdl, 0, true); $convert = new ConvertXml(); // var_dump($convert->xmlToArray($xml)); // array(37) { // [0]=> // array(1) { // ["city"]=> // array(2) { // ["@attributes"]=> // array(9) { // ["quName"]=> // string(9) "黑龙江" // ["pyName"]=> // string(12) "heilongjiang" // ["cityname"]=> // string(9) "哈尔滨" // ["state1"]=> // string(1) "7" // ["state2"]=> // string(1) "3" // ["stateDetailed"]=> // string(15) "小雨转阵雨" // ["tem1"]=> // string(2) "21" // ["tem2"]=> // string(2) "16" // ["windState"]=> // string(21) "南风6-7级转4-5级" // } // ["value"]=> // string(0) "" // } // } // [1]=> // array(1) { // ["city"]=> // array(2) {
在這裡,我們使用的是 SimpleXMLIterator 物件。從名稱中就可以看出,它的作用是產生可以遍歷的 SimpleXMLElement 物件。第一個參數是格式正確的 XML 文字或連結地址。第二個參數是一些選項參數,這裡我們直接給 0 就好了。第三個參數則是指明第一個參數是否是連結位址,這裡我們給 true 。
我們在客戶端產生了 SimpleXMLIterator 對象,並傳遞到 xmlToArray() 方法中。這樣 SimpleXMLIterator 物件就能讓我們遍歷各個結點了,接下來的事情就很簡單了,我們只需要判斷一下結點是否還有子結點,如果有子結點則遞歸調用當前這個方法。如果沒有子結點了,就取得結點的屬性和內容。
這個測試連結是取得天氣資訊的,回傳的內容中每個結點都只有屬性沒有內容,體現在轉換後的陣列中就是 value 欄位都是空的。
PHP 陣列或物件轉換為 XML
class ConvertXml{ // ...... const UNKNOWN_KEY = 'unknow'; public function arrayToXml(array $a) { $xml = new SimpleXMLElement('<?xml version="1.0" standalone="yes"?><root></root>'); $this->phpToXml($a, $xml); return $xml->asXML(); } protected function phpToXml($value, &$xml) { $node = $value; if (is_object($node)) { $node = get_object_vars($node); } if (is_array($node)) { foreach ($node as $k => $v) { if (is_numeric($k)) { $k = 'number' . $k; } if (!is_array($v) && !is_object($v)) { $xml->addChild($k, $v); } else { $newNode = $xml->addChild($k); $this->phpToXml($v, $newNode); } } } else { $xml->addChild(self::UNKNOWN_KEY, $node); } } } var_dump($convert->arrayToXml($data)); // string(84454) "<?xml version="1.0" standalone="yes"?> // <root><unlikely-outliner><subject><mongo-db><outline><chapter><getting-started><number0> ........... // "
我們在 arrayToXml() 中,先使用 SimpleXMLElement 物件建立了一個基本的根結點結構。然後使用 phpToXml() 方法來建立所有結點。為什麼要拆成兩個方法呢?因為 phpToXml() 方法是需要遞歸呼叫的,在每次遞歸的時候我們不需要重新的去創建根結點,只需要在根結點下面使用 addChild() 添加子結點就可以了。
在 phpToXml() 的程式碼中,我們也使用了 get_object_vars() 函數。就是當傳遞進來的數組項內容是物件時,透過這個函數可以取得物件的所有屬性。如果將物件看做是一個陣列的話,每個屬性值就是它的鍵值對。
在對每個鍵值遍歷時,我們判斷目前的鍵對應的內容是否是陣列或是物件。如果不是這兩種形式的內容的話,就直接將目前的內容加入為當前結點的子結點。如果是陣列或物件的話,就繼續遞歸地加入直到陣列內容全部遍歷完成。
測試的 $data 內容非常長,大家可以直接透過測試程式碼的連結去 Github 上查閱。
總結
這篇文章的內容是簡單的學習了一個 SPL 擴充庫中對於 XML 操作的兩個物件的使用。透過它們,我們可以方便的轉換 XML 資料格式。當然,對於 XML 的格式轉換來說,我們還有它的方法,以後學到了再說!
測試程式碼:
https://github.com/zhangyue0503/dev-blog/blob/master/php/202009/source/在PHP中使用SPL函式庫中的物件方法進行XML與陣列的轉換.php