雖說現在很多的服務提供商都會提供 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 的格式轉換來說,我們還有其它的方法,以後學到了再說!
測試代碼:
參考文檔:
《PHP7編程實戰》
各自媒體平臺均可搜索【硬核項目經理】