SimpleXML& PHP:提取部分XML文档&转换为数组

时间:2011-08-10 17:23:50

标签: php xml xpath simplexml

考虑以下XML:

<?xml version="1.0" encoding="UTF-8"?>
<OS>
    <data>
        <OSes>
            <centos>
                <v_5>
                    <i386>
                        <id>centos5-32</id>
                        <name>CentOS 5 - 32 bit</name>
                        <version>5</version>
                        <architecture>32</architecture>
                        <os>centos</os>
                    </i386>
                    <x86_64>
                        <id>centos5-64</id>
                        <name>CentOS 5 - 64 bit</name>
                        <version>5</version>
                        <architecture>64</architecture>
                        <os>centos</os>
                    </x86_64>
                </v_5>
                <v_6>
                    <i386>
                        <id>centos6-32</id>
                        <name>CentOS 6 - 32 bit</name>
                        <version>6</version>
                        <architecture>32</architecture>
                        <os>centos</os>
                    </i386>
                    <x86_64>
                        <id>centos6-64</id>
                        <name>CentOS 6 - 64 bit</name>
                        <version>6</version>
                        <architecture>64</architecture>
                        <os>centos</os>
                    </x86_64>
                </v_6>
            </centos>
            <ubuntu>
                <v_10>
                    <i386>
                        <id>ubuntu10-32</id>
                        <name>Ubuntu 10 - 32 bit</name>
                        <version>10</version>
                        <architecture>32</architecture>
                        <os>ubuntu</os>
                    </i386>
                    <amd64>
                        <id>ubuntu10-64</id>
                        <name>Ubuntu 10 - 64 bit</name>
                        <version>10</version>
                        <architecture>64</architecture>
                        <os>ubuntu</os>
                    </amd64>
                </v_10>
            </ubuntu>
        </OSes>
    </data>
</OS>

从上面的XML文档中,我想提取以下5个元素节点

  1. <id>
  2. <name>
  3. <version>
  4. <architecture>
  5. <os>
  6. 将它们作为数组。我尝试了以下操作:

    <?php 
    require_once "xml.php";
    
        try {
            $xml = new SimpleXMLElement($xmlstr);
            foreach($xml->xpath(' //id | //name | //version// | //architecture | //os ') as $record) {
            echo $record;
        }
        } catch(Exception $e){
            echo $e->getMessage();
        }
    

    上面的代码有效,但每条记录都是一个单独的对象。我希望有人将所有5个元素节点合并为一个数组元素。像这样的东西:

    $osList = Array( [0] => Array(
                                   ["id"] => "<id>",
                                   ["name"] => "<name>",
                                   ["version"] => "<version>",
                                   ....
    )
     .....
    );
    

    语法不正确,但你明白了。知道怎么做吗?

3 个答案:

答案 0 :(得分:3)

这可能会有所帮助

$obj = new SimpleXMLElement($xml);
$rtn = array();
$cnt = 0;
foreach($obj->xpath('///OSes/*/*') as $rec)
{
  foreach ($rec as $rec_obj)
  {
    if (!isset($rtn[$cnt]))
    {
      $rtn[$cnt] = array();
    }

    foreach ($rec_obj as $name=>$val)
    {
      $rtn[$cnt][(string)$name] = (string)$val;
    }
    ++$cnt;
  }
}

答案 1 :(得分:2)

通过修改其他人建议的xpath,我得出了这个结论。它使用一个辅助函数来重新格式化每个xpath结果节点,并使用array_reduce迭代结果。然后它返回转换后的结果(Demo):

$xml = new SimpleXMLElement($xmlstr);
$elements = array_reduce(
    $xml->xpath('//OSes/*/*'),
    function($v, $w) {
        $w = array_values((array) $w); // convert result to array
        foreach($w as &$d) $d = (array) $d; // convert inner elements to array
        return array_merge($v, $w); // merge with existing
    }, 
    array() // empty elements at start
);

输出:

Array
(
    [0] => Array
        (
            [id] => centos5-32
            [name] => CentOS 5 - 32 bit
            [version] => 5
            [architecture] => 32
            [os] => centos
        )

    [1] => Array
        (
            [id] => centos5-64
            [name] => CentOS 5 - 64 bit
            [version] => 5
            [architecture] => 64
            [os] => centos
        )

    [2] => Array
        (
            [id] => centos6-32
            [name] => CentOS 6 - 32 bit
            [version] => 6
            [architecture] => 32
            [os] => centos
        )

    [3] => Array
        (
            [id] => centos6-64
            [name] => CentOS 6 - 64 bit
            [version] => 6
            [architecture] => 64
            [os] => centos
        )

    [4] => Array
        (
            [id] => ubuntu10-32
            [name] => Ubuntu 10 - 32 bit
            [version] => 10
            [architecture] => 32
            [os] => ubuntu
        )

    [5] => Array
        (
            [id] => ubuntu10-64
            [name] => Ubuntu 10 - 64 bit
            [version] => 10
            [architecture] => 64
            [os] => ubuntu
        )

)

我还选择将原始xpath结果转换为两个级别的数组,每次在当前级别内已存在一个键,将当前条目移动到新条目(Demo):

try
{
    $xml = new SimpleXMLElement($xmlstr);
    $elements = array();
    $curr = NULL;
    foreach($xml->xpath('//id | //name | //version | //architecture | //os') as $record)
    {
        $key = $record->getName();
        $value = (string) $record;
        if (!$curr || array_key_exists($key, $curr)) {
            unset($curr);
            $curr = array();
            $elements[] = &$curr;
        }
        $curr[$key] = $value;
    }
    unset($curr);
}
catch(Exception $e)
{
    echo $e->getMessage();
}

结果如下:

Array
(
    [0] => Array
        (
            [id] => centos5-32
            [name] => CentOS 5 - 32 bit
            [version] => 5
            [architecture] => 32
            [os] => centos
        )

    [1] => Array
        (
            [id] => centos5-64
            [name] => CentOS 5 - 64 bit
            [version] => 5
            [architecture] => 64
            [os] => centos
        )

    [2] => Array
        (
            [id] => centos6-32
            [name] => CentOS 6 - 32 bit
            [version] => 6
            [architecture] => 32
            [os] => centos
        )

    [3] => Array
        (
            [id] => centos6-64
            [name] => CentOS 6 - 64 bit
            [version] => 6
            [architecture] => 64
            [os] => centos
        )

    [4] => Array
        (
            [id] => ubuntu10-32
            [name] => Ubuntu 10 - 32 bit
            [version] => 10
            [architecture] => 32
            [os] => ubuntu
        )

    [5] => Array
        (
            [id] => ubuntu10-64
            [name] => Ubuntu 10 - 64 bit
            [version] => 10
            [architecture] => 64
            [os] => ubuntu
        )

)

答案 2 :(得分:1)

试试这个:

// flatten:
function arrayval1($any) {
  return array_values((array)$any);
}
function arrayval2($any) {
  return (array)$any;
}

// xml objects with xml objects:
$oses = $xml->xpath('//OSes/*/*');
// an array of xml objects:
$oses = array_map('arrayval1', $oses);
// merge to a flat array:
$oses = call_user_func_array('array_merge', $oses);
// xml objects -> arrays
$oses = array_map('arrayval2', $oses);
print_r($oses);

我的结果:

Array
(
    [0] => Array
        (
            [id] => centos5-32
            [name] => CentOS 5 - 32 bit
            [version] => 5
            [architecture] => 32
            [os] => centos
        )

    [1] => Array
        (
            [id] => centos5-64
            [name] => CentOS 5 - 64 bit
            [version] => 5
            [architecture] => 64
            [os] => centos
        )

    [2] => Array
        (
            [id] => centos6-32
            [name] => CentOS 6 - 32 bit
            [version] => 6
            [architecture] => 32
            [os] => centos
        )

    [3] => Array
        (
            [id] => centos6-64
            [name] => CentOS 6 - 64 bit
            [version] => 6
            [architecture] => 64
            [os] => centos
        )

    [4] => Array
        (
            [id] => ubuntu10-32
            [name] => Ubuntu 10 - 32 bit
            [version] => 10
            [architecture] => 32
            [os] => ubuntu
        )

    [5] => Array
        (
            [id] => ubuntu10-64
            [name] => Ubuntu 10 - 64 bit
            [version] => 10
            [architecture] => 64
            [os] => ubuntu
        )

)

如果您正在使用PHP&gt; = 5.3(当然是你,为什么不是你)你可以省略讨厌的tmp函数定义并使用酷匿名函数进行映射:

// an array of xml objects:
$oses = array_map(function($os) {
  return array_values((array)$os);
}, $oses);