一个简单的XML与数组之间的转换

     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则称为属性值),后边的元素值将覆盖前面的元素的值,这也是开头说的的一个传递数组的小例子时,里边没有相同元素名的元素。

      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转数组时,需要遍历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);

    打印结果:

     技术分享

    如果同一代下边有相同的元素名的,后边会覆盖前边,因此使用范围还是有限。

 

      

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。