xml是网络使用最多的数据交换格式,所以,不掌握怎么操作它,又有蛋疼的了。

php中可以操作xml的类/函数很多,个人认为最简单的是SimpleXMLElement这个类,它的使用就跟其名字一样:简单。当然要想全面自如的操作xml,还得借助其他的类。SimpleXMLElement主要是对xml的进行节点的添加和获取,以及输出整个xml文本内容,但是对于实现一个简单的与数组之间的内容转换,已经足够了。

比如我们现在在接一个sdk,对方接口传过来的是一个简单的xml格式数据,我们需要取到它,并作一些处理,最后又返回给它,而且返回的仍是xml格式。能让我们处理的游刃有余的格式,当然是数组为最好的。

首先声明,这个方法能处理的xml的格式比较特殊(待会儿会说这个代码不能处理的情况),如下面这样的:

<response>

<title>entity</title>

<name>toy</name>

<price>32.00</price>

<date>2014-01-03 15:25:36</date>

</response>

首先它必须包含在一个总的开闭标签内,当然,这是xml必须格式。然后它的元素名必须是非数字字符串,而且还不能有重复标签元素,限制得很死,所以只能作为局部简单的数据交换,真正说要传送复杂的xml文件,玩不转。

在SimpleXMLElement这个类中,有个addChild方法,功能是添加xml节点,添加时可以返回指向该节点对象的变量。SimpleXMLElement类的构造函数或者是simplexml_load_string、simplexml_load_file方法,允许我们从另外一个文件或者从一个xml字符串来构造一个SimpleXMLElement类,它的asXML则方法返回一个标准化好的xml格式字符串数据。比如现在有一个空的、字符串xml格式数据"<xml><a>hello</a></xml>",使用addChild方法是这样:

    $str = '<xml><a>hello</a></xml>';
$sxe = simplexml_load_string($str);
if($sxe){
$cha = $sxe->addChild('cha'); // 添加一个空的标签元素
$cha->addChild('person', 'Jack');
$cha->addChild('person', 'Perter'); // 添加两个person标签,值为对应的第二个参数
echo $sxe->asXML();
}

以asXML格式输出是这么个样子

全堆在一块儿了,asXML方法只返回节点的文本内容,即节点的字符串值,所以看不出xml结构。右键查看源代码或者chrome审查元素就能看到它的原貌:

<xml>元素是整个xml数据的根节点,一开始里边只有<a>元素,<cha>元素和它的两个子元素<person>是通过代码添加上去的,使用的就是addChild方法。

转换的大致思路是,从数组转为xml时,以键作为元素名,对应的值作为该元素的值, 反过来从xml转为数组,以元素名作为数组的键,元素值作为对应的值。存在的问题是,xml不允许纯数字作为元素名,如果数组中有数字或数字字符串作为键名的,转为xml时会失败;从xml转数组时,若是xml中同一代节点(处在同一级别)中有相同元素名的(<a type="ok">aaa</a>,我喜欢将a称为元素名,aaa称为元素值,type称为属性名,ok则称为属性值),后边的元素值将覆盖前面的元素的值,这也是开头说的的一个传递数组的小例子时,里边没有相同元素名的元素。

  由于xml转数组时,元素名一般不为数字,为字符串又相同时,可能导致数组的键名相同,值遭到覆盖(除非相同的转为数组),所以一般去请求别人的接口拿数据时,每个字段的元素名都不一样,代表不同的含义,这时可以忽略这些可能出现的bug。

1. 数组转xml

首先foreach数组,取得键和值,如果值是标量(当然一般也不会传递对象等等类型,除非序列化等),直接将元素加入SimpleXMLElement对象,元素名是键名,元素值时键对应的值;如果值是数组,则先加入一个空的元素,元素名是键名,然后它对应的数组再次按照前面的步骤处理,很明显需要递归。

<?php
/**
数组转为XML
@param arr array
@param arr NULL|SimpleXMLElement
return string
*/
function arrayToXml($arr, $sxe = NULL){
$str = '<xmlData></xmlData>';
if($sxe instanceOf SimpleXMLElement){
$xmlDoc = $sxe;
}
else{
$xmlDoc = new SimpleXMLElement($str);
} foreach($arr as $key => $val){
if(is_array($val)){
$child = $xmlDoc->addChild($key);
arrayToXml($val, $child);
}
else{
$xmlDoc->addChild($key, $val);
}
return $xmlDoc->asXML();
}
//测试
$arr = array('a'=>'aaa', 'b'=>'bbb', 'c'=>array('d'=>1, 'e'=>2, 'f'=>'fff'));
$xml = arrayToXml($arr);
echo $xml.'<br/>';

效果:

    

<b>元素被处理成了HTML标签。如果要做点改进的话,一是可以处理下数组中键为数字或数字字符串的情况,比如同一给一个名字num,因为xml数据中允许多个相同的元素名存在;二是我们本地的编辑器在码代码的时候可能编码不是对方需要的,可以做一下编码转换,再传输给对方,下面是改进后的

<?php
/**
数组转为XML
@param arr array
@param arr NULL|SimpleXMLElement
return string
*/
function arrayToXml($arr, $sxe = NULL){
$str = '<xmlData></xmlData>';
if($sxe instanceOf SimpleXMLElement){
$xmlDoc = $sxe;
}
else{
$xmlDoc = new SimpleXMLElement($str);
} foreach($arr as $key => $val){
if(is_array($val)){ if(is_numeric($key)){ // 一般来说XML中的标签都是带字母的字符串
$child = $xmlDoc->addChild('num'); // 添加新标签,并返回指向该新标签的变量
}
else{
$child = $xmlDoc->addChild($key);
}
arrayToXml($val, $child); // 以当前子节点为对象,在该节点基础上插入子数组中的元素
}
else{
$val = mb_convert_encoding($val, 'UTF-8');
if(is_numeric($key)){
$xmlDoc->addChild('num', $val);
}
else{
$xmlDoc->addChild($key, $val);
}
}
}
return $xmlDoc->asXML();
}
//测试
$arr = array('a'=>'aaa', 5, '1'=>'bbb', 'c'=>array(1, 2, 'f'=>'fff'));
$xml = arrayToXml($arr);
echo $xml.'<br/>';

效果:

   

可以看到数字键名均替换成了num元素名,xml相同的元素名不会覆盖。

2. xml转数组

  还是先拿简单的说,比如需要转化的xml数据结构很简单,子节点都只有一层:

<response>
<order_id>123</order_id>
<cost>68</cost>
</response>

  这时大可不必用什么遍历,而是先转对象再取对象属性,或者强制转为数组。

    $xmlObj = simplexml_load_string('<response><order_id>123</order_id><cost>68</cost></response>');
$arr = get_object_vars($xmlObj); // 取对象属性
echo 'arr=><pre>';
var_dump($arr);
$arr2 = (array)$xmlObj; // 或者,强制转换为数组
// echo 'arr2=><pre>';
// var_dump($arr2);

如果需要处理的xml字符串不像上面那么简单, 或者你想玩点稍微复杂点的,则需要遍历xml元素节点,取它们的元素名和元素值等一步步处理。SimpleXMLElement类本身不具备遍历的方法,可以用到它的子类SimpleXMLIterator,一看名字就知道这个类专为迭代、遍历准备的。它不但集成SimpleXMLElement,还实现了递归迭代器和统计等接口,在遍历一个xml对象时,主要用到一下几个方法:

current(void)    返回当前元素

key(viod)     返回当前元素名

hasChildren(void)     判断当前元素是否有子元素

valid(void)   判断当前当前指向元素是否有效,遍历到头指向对象最后一个元素的下一个则无效

next(void)    指向下一个元素

rewind(void)    设置指向对象最开头的元素

需要注意的是,指向对象的第一个元素,是指向如下边中的<a>,而不是<xml>,<xml>是将整个xml数据包含起来的元素,这点需要注意,当然若想仔细学习下xml,还得专门看看它的定义使用,当然还有很多别的可以设置的地方

<xml>

<a>a</a>

<b>b</b>

</xml>

转换思路是,如果传进来的不是对象(最开始肯定是传递一个xml字符串进来),先创建SimpleXMLIterator对象,然后遍历该对象元素,如果该元素没有子元素,将它的元素名作为键,元素值作为对应值放入数组;如果有子元素,将它的元素名取出来,作为键,而将该子元素变量传入该方法, 递归返回,代码如下

<?php
/**
XML转数组
@param xml string|SimpleXMLIterator
return array
*/
function xmlToArray($xml){
$ret = array();
if($xml instanceOf SimpleXMLElement){
$xmlDoc = $xml;
}
else{
$xmlDoc = simplexml_load_string($xml, 'SimpleXMLIterator');
if(!$xmlDoc){ // xml字符串格式有问题
return null;
}
} for($xmlDoc->rewind(); $xmlDoc->valid(); $xmlDoc->next()){
$key = $xmlDoc->key(); // 获取标签名
$val = $xmlDoc->current(); // 获取当前标签
if($xmlDoc->hasChildren()){ // 如果有子元素
$ret[$key] = xmlToArray($val); // 子元素变量递归处理返回
}
else{
$ret[$key] = (string)$val;
}
}
return $ret;
}
$xmlstr = '<xml><name>cat</name><color>black</color><feature><weight>1.0kg</weight><height>0.25m</height><hobby>sleep</hobby></feature></xml>';
$array = xmlToArray($xmlstr);
echo 'xml to array=><pre>';
print_r($array);

打印结果:

  但是只用SimpleXMLElement对象实际上也能够做到,主要用到

  children:获取xml元素的子节点,比如上边名为xml的元素的子节点,不只一个,虽然它返回的仍是一个SimpleXMLElement对象,但是这个对象是可迭代的,既可以用foreach遍历每一个子节点。同时,如果是单个Element节点,这个函数返回空,此时就可以取它的元素值了作为数组元素的值返回了,直接转为string即可。

  getName:获取元素名,这样可以得到转为数组中键名  

<?php
function xmlToArr($xml)
{
if (!($xml->children())) // 单个子元素,转为字符串返回
{
return (string) $xml;
}
// 直接遍历子元素对象
foreach ($xml->children() as $child)
{
$name = $child->getName(); // 获取元素名
if (count($xml->$name) ==1 )
{
$element[$name] = xmlToArr($child);
}
else // 如果同名子元素有多个,装在一个数组里边
{
$element[][$name] =xmlToArr($child);
}
}
return $element;
}

  上边这个方法将同名子节点保存成了数组,也可以按自己的需要以其他的方式组织,需要传入一个SimpleXMLElement对象。

  当然以上仅仅是简单的使用方式,xml实际上还有命名空间、名称前缀等等,还是得看看xml是则么定义的

一个简单的XML与数组之间的转换的更多相关文章

  1. 最简单的XML转数组

    /** * 最简单的XML转数组 * @param string $xmlstring XML字符串 * @return array XML数组 */ function simplest_xml_to ...

  2. php中 xml json 数组 之间相互转换

    php中 xml json  数组 之间相互转换 1 数组转json $result = array( 'status' =>$status, 'message'=>$message, ' ...

  3. JAVA中list,set,数组之间的转换详解

    JAVA的list,set,数组之间的转换,主要是使用Apache Jakarta Commons Collections,具体的方法如下:import org.apache.commons.coll ...

  4. C# 16进制与字符串、字节数组之间的转换(转)

    1.请问c#中如何将十进制数的字符串转化成十六进制数的字符串   //十进制转二进制 Console.WriteLine("十进制166的二进制表示: "+Convert.ToSt ...

  5. Java 集合 集合与数组之间的转换

    Java 集合 集合与数组之间的转换 @author ixenos 数组转集合 Arrays.asList(T... a) 先给结论:用 Arrays.asList(T... a) 将数组转换成集合 ...

  6. C# 16进制与字符串、字节数组之间的转换 (转载)

    1.请问c#中如何将十进制数的字符串转化成十六进制数的字符串 //十进制转二进制 Console.WriteLine(, )); //十进制转八进制 Console.WriteLine(, )); / ...

  7. C#串口通讯,16进制与字符串、字节数组之间的转换。

    1.将十进制数的字符串转化成十六进制数的字符串 //十进制转二进制Console.WriteLine("十进制166的二进制表示: "+Convert.ToString(166, ...

  8. C# 16进制与字符串、字节数组之间的转换

    1.请问c#中如何将十进制数的字符串转化成十六进制数的字符串//十进制转二进制 Console.WriteLine("十进制166的二进制表示: "+Convert.ToStrin ...

  9. java中 列表,集合,数组之间的转换

    java中 列表,集合,数组之间的转换 java中 列表,集合,数组之间的转换 java中 列表,集合,数组之间的转换 List和Set都是接口,它们继承Collection(集合),集合里面任何数据 ...

随机推荐

  1. jQuery基础学习5——JavaScript方法获取页面中的元素

    给网页中的所有<p>元素添加onclick事件 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN& ...

  2. javascript 获取HTML DOM父、子、临近节点

    在Web应用程序特别是Web2.0程序开发中,经常要获取页面中某个元素,然后更新该元素的样式.内容等.如何获取要更新的元素,是首先要解决的问题.令人欣慰的是,使用JavaScript获取节点的方法有很 ...

  3. Qt 自动搜索串口号列表

    @功能: SerialPortList 类实现当前可用的串口进行实时扫描,当发现有新的串口 或是现有串口消失时,SerialPortList类将发出一个QStringList类型的 信号onNewSe ...

  4. [转]directsound抓取麦克风PCM数据封装类

    网上有很多方法从麦克风读取PCM数据,不想一一举例.只是在这里发布一个我自己写的directsound的麦克风PCM数据采集类,通过它,可以很方便的利用directsound技术把麦克风的数据采集到, ...

  5. 树莓派加入定时任务实现花生壳定时重启(linux的定时任务)

    由于花生壳在linux下不稳定,联系开机一个星期左右会挂掉,所以要使用定时任务实现每小时刷新一次/启动一次. 使用的是linux下的定时任务crontab去实现. 实现步骤: 1.编辑/etc/cro ...

  6. securecrt 连接vmware ubuntu

    折腾了好几天,我只想说shit,吃一堑长一智,和大家分享. SecureCRT连接Linux是使用Ubuntu下的SSH服务,ssh包括客户端和服务端即openssh-client,openssh-s ...

  7. Flex Alert.show()方法的详解

    本文和大家重点讨论一下Flex Alert.show()flag详细值,Flex Alert.show()里面有多个属性,其中排在第三是flags,这个属性作用是在弹出的Alert提示框里面显示那一个 ...

  8. Codeforces Gym 100523E E - Gophers SET

    E - GophersTime Limit: 20 Sec Memory Limit: 256 MB 题目连接 http://acm.hust.edu.cn/vjudge/contest/view.a ...

  9. IE8不显示字体图标

    bootstrap流行,随着自带的字体图标也火起来了.美丽的字体系统中没有.制作成字体文件,下载到本地.浏览美丽的网页哦. 在项目中遇到有些IE8显示不了,原因是IE8下设置了禁止字体下载

  10. [Codeforces] 347B - Fixed Points

    题意:给定一个序列,现有一种操作:两个数的位置互换.问最多操作一次.序列 [元素位置i]  与 [元素Ai] 相等的最多个数? 依据题意,最多个数为 : [操作之前[元素位置i]  与 [元素Ai] ...