如何将数组转换为SimpleXML

309

如何在PHP中将数组转换为SimpleXML对象?


6
一个什么的数组? - cletus
1
这很酷:http://www.viper007bond.com/2011/06/29/easily-create-xml-in-php-using-a-data-array/ - Arvind Bhardwaj
34个回答

421

以下是php 5.2代码,可以将任意深度的数组转换为XML文档:

Array
(
    ['total_stud']=> 500
    [0] => Array
        (
            [student] => Array
                (
                    [id] => 1
                    [name] => abc
                    [address] => Array
                        (
                            [city]=>Pune
                            [zip]=>411006
                        )                       
                )
        )
    [1] => Array
        (
            [student] => Array
                (
                    [id] => 2
                    [name] => xyz
                    [address] => Array
                        (
                            [city]=>Mumbai
                            [zip]=>400906
                        )   
                )

        )
)

生成的XML将会是:

<?xml version="1.0"?>
<student_info>
    <total_stud>500</total_stud>
    <student>
        <id>1</id>
        <name>abc</name>
        <address>
            <city>Pune</city>
            <zip>411006</zip>
        </address>
    </student>
    <student>
        <id>1</id>
        <name>abc</name>
        <address>
            <city>Mumbai</city>
            <zip>400906</zip>
        </address>
    </student>
</student_info>

PHP代码片段

<?php
// function defination to convert array to xml
function array_to_xml( $data, &$xml_data ) {
    foreach( $data as $key => $value ) {
        if( is_array($value) ) {
            if( is_numeric($key) ){
                $key = 'item'.$key; //dealing with <0/>..<n/> issues
            }
            $subnode = $xml_data->addChild($key);
            array_to_xml($value, $subnode);
        } else {
            $xml_data->addChild("$key",htmlspecialchars("$value"));
        }
     }
}

// initializing or creating array
$data = array('total_stud' => 500);

// creating object of SimpleXMLElement
$xml_data = new SimpleXMLElement('<?xml version="1.0"?><data></data>');

// function call to convert array to xml
array_to_xml($data,$xml_data);

//saving generated xml file; 
$result = $xml_data->asXML('/file/path/name.xml');

?>

在此片段中使用的SimpleXMLElement::asXML的文档


3
这个例子使用htmlspecialchars明确转义元素文本数据中的特殊字符,但SimpleXMLElement :: addChild会自动将xml特殊字符转换为它们的字符实体,因此可以省略htmlspecialchars。有趣的是,这似乎不会导致双重转义数据。 - mbaynton
1
空的数组值[(字符串)""]将被更改为一个空的SimpleXML节点,而不是保持为空。 - Jonathan
3
@Alex,你的第五次编辑使得这个例子失效了。它在每个<student>记录之前插入了<item$x>,导致XML输出结果不是作者原本想要的。也许你可以提供一个你试图解决的问题的例子,这样我们可以为两种情况找到另外一个解决方案。我花了一段时间才意识到作者的代码已经被修改了。 - Nicholas Blasgen
1
我该如何删除itemxxx节点? - bart
5
需要发布两个答案,这个修改后的答案没有按照我的要求,它添加了 <itemN></itemN>。这个修订版:http://stackoverflow.com/revisions/5965940/2 是我选择的获胜者。 - zanderwar
显示剩余14条评论

227
一个短的:
<?php

$test_array = array (
  'bla' => 'blub',
  'foo' => 'bar',
  'another_array' => array (
    'stack' => 'overflow',
  ),
);
$xml = new SimpleXMLElement('<root/>');
array_walk_recursive($test_array, array ($xml, 'addChild'));
print $xml->asXML();

导致结果

<?xml version="1.0"?>
<root>
  <blub>bla</blub>
  <bar>foo</bar>
  <overflow>stack</overflow>
</root>

键和值被交换了 - 你可以在 array_walk 之前用 array_flip() 修复它。array_walk_recursive 需要 PHP 5。你可以使用 array_walk 替代,但是你将无法在 XML 中获得 'stack' => 'overflow'


56
如果$test_array中有'more_another_array',例如'another_array',那么这将行不通,因为键'another_array'没有转换。 因此,您将拥有多个'<overflow>stack</ overflow>'。 - understack
11
array_flip函数无法翻转数组(例如主数组中的another_array),因此不能使用该函数。 - Lode
15
"another_array" 这个 XML 元素在哪里?所有内容都被展平了 :( - FMaz008
16
因为array_flip只在数组中不包含相同值时才有效,所以会被downvote。 - Martijn
3
下投票 - 尽管表面上看起来很精致,但需要使用array_flip使其变得相当无用。 - Jim Morrison
显示剩余8条评论

144
这里提供的答案仅将数组转换为XML节点,您无法设置属性。我编写了一个PHP函数,允许您将数组转换为XML,并为XML中特定节点设置属性。缺点是您必须按照一些约定构造数组(仅在使用属性时需要)。
以下示例还允许您在XML中设置属性。
源代码可以在此处找到: https://github.com/digitickets/lalit/blob/master/src/Array2XML.php
<?php    
$books = array(
    '@attributes' => array(
        'type' => 'fiction'
    ),
    'book' => array(
        array(
            '@attributes' => array(
                'author' => 'George Orwell'
            ),
            'title' => '1984'
        ),
        array(
            '@attributes' => array(
                'author' => 'Isaac Asimov'
            ),
            'title' => 'Foundation',
            'price' => '$15.61'
        ),
        array(
            '@attributes' => array(
                'author' => 'Robert A Heinlein'
            ),
            'title' => 'Stranger in a Strange Land',
            'price' => array(
                '@attributes' => array(
                    'discount' => '10%'
                ),
                '@value' => '$18.00'
            )
        )
    )
);
/* creates 
<books type="fiction">
  <book author="George Orwell">
    <title>1984</title>
  </book>
  <book author="Isaac Asimov">
    <title>Foundation</title>
    <price>$15.61</price>
  </book>
  <book author="Robert A Heinlein">
    <title>Stranger in a Strange Land</title>
    <price discount="10%">$18.00</price>
  </book>
</books>
*/
?>

11
我惊讶于没有人对这个发表评论。这个类非常有用,因为它可以生成与simpleXMLElement相反的结果,所以它使得你可以双向使用SimpleXMLElement。 - FMaz008
4
我会将其标记为答案,而不是当前的选项。当前的答案不能构建递归数组。 - Oleksandr IY
2
不错的类。我将第128行的 if(!is_array($arr)) { 更改为 if(!is_array($arr) && $arr !== '') {,这样它就不会为空字符串附加新的文本节点,因此保留了简写的空标签格式,即 'tag'=>''<tag/> 而不是 <tag></tag> - user1433150
到目前为止,这是最好的答案。此外,它具有相同键的多个项的正确结构:首先是节点名称键,然后它包含具有数字键的数组。(与Hanmant答案相反) - Vasil Popov
1
发现了作者@Legionar 的Githubhttps://github.com/digitickets/lalit/blob/master/src/Array2XML.php - Daryl Teo
显示剩余2条评论

68

我发现所有的答案都使用了太多的代码。这里有一个简单的方法:

function to_xml(SimpleXMLElement $object, array $data) {
    foreach ($data as $key => $value) {
        // if the key is an integer, it needs text with it to actually work.
        $valid_key  = is_numeric($key) ? "key_$key" : $key;
        $new_object = $object->addChild( 
            $valid_key, 
            is_array($value) ? null : htmlspecialchars($value) 
        );

        if (is_array($value)) {
            to_xml($new_object, $value);
        }
    }
}

然后只需将数组发送到使用递归的函数中,这样它就可以处理多维数组:

$xml = new SimpleXMLElement('<rootTag/>');
to_xml($xml, $my_array);

现在,$xml 变量包含了一个美丽的 XML 对象,该对象基于你编写的数组而生成。
print $xml->asXML();

10
我最喜欢这个解决方案。不过,如果在数字键上添加一个测试会更好,比如:if ( is_numeric( $key ) ) $key = "numeric_$key"; - wout
@wout 很好的发现。已添加。我进行了 int 强制转换检查,而不是使用 is_numeric,因为 is_numeric 可能会给出一些虽然在技术上是预期的结果,但实际上会让你感到困惑。 - Francis Lewis
我也最喜欢这个解决方案,简单易行 :-)一个备注:你可能想把 $object->addChild($key, $value); 改成 $object->addChild($key, htmlspecialchars($value));,以防止当 $value 包含像 "&" 这样需要 XML 编码的字符时导致失败。 - leo
@Valeri 如果使用 ===,那么你会面临将数字作为字符串尝试创建数组键的情况,这将导致失败。这就是为什么它无法进行类型检查的原因。 - Francis Lewis
太好了!为我工作。谢谢。 - Taffarel Xavier
显示剩余7条评论

39
$v) { is_array($v) ? array_to_xml($v, $xml->addChild($k)) : $xml->addChild($k, $v); } return $xml; } $test_array = array ( 'bla' => 'blub', 'foo' => 'bar', 'another_array' => array ( 'stack' => 'overflow', ), );
// 将数组转换为XML,并输出结果 echo array_to_xml($test_array, new SimpleXMLElement(''))->asXML(); ?>

2
如果您的数组包含具有数字索引的内部数组,则此方法将失败。"<0>...</0>" 不是有效的 XML。 - Adriano Varoli Piazza
@AdrianoVaroliPiazza 只需在 foreach() 内部添加类似 $k = (is_numeric($k)) ? 'item' : $k; 的内容即可。 - AlienWebguy
如果数组中的一个键名为“body”,它就无法正常工作——更准确地说,该键会被忽略和遍历。正在尝试找出原因。 - Bambax
@Bambax 我能想到的唯一原因是如果 XML 在稍后某个时候被解析为 HTML。 - Brilliand

19

自 PHP 5.4 版本开始

function array2xml($data, $root = null){
    $xml = new SimpleXMLElement($root ? '<' . $root . '/>' : '<root/>');
    array_walk_recursive($data, function($value, $key)use($xml){
        $xml->addChild($key, $value);
    });
    return $xml->asXML();
}

看起来这只是从所选答案中直接复制粘贴到一个函数中。 - phaberest
我会在addChild部分添加htmlspecialchars(),像这样:$xml->addChild($key, htmlspecialchars($value)); - Tyreal

15

另一个改进:

/**
* Converts an array to XML
*
* @param array $array
* @param SimpleXMLElement $xml
* @param string $child_name
*
* @return SimpleXMLElement $xml
*/
public function arrayToXML($array, SimpleXMLElement $xml, $child_name)
{
    foreach ($array as $k => $v) {
        if(is_array($v)) {
            (is_int($k)) ? $this->arrayToXML($v, $xml->addChild($child_name), $v) : $this->arrayToXML($v, $xml->addChild(strtolower($k)), $child_name);
        } else {
            (is_int($k)) ? $xml->addChild($child_name, $v) : $xml->addChild(strtolower($k), $v);
        }
    }

    return $xml->asXML();
}

使用方法:

$this->arrayToXML($array, new SimpleXMLElement('<root/>'), 'child_name_to_replace_numeric_integers');

谢谢!您的函数返回任何n维数组的精确内容。 - besciualex

12

这是我的入口,简单清晰...

function array2xml($array, $xml = false){
    if($xml === false){
        $xml = new SimpleXMLElement('<root/>');
    }
    foreach($array as $key => $value){
        if(is_array($value)){
            array2xml($value, $xml->addChild($key));
        }else{
            $xml->addChild($key, $value);
        }
    }
    return $xml->asXML();
}


header('Content-type: text/xml');
print array2xml($array);

9

总之...我采用了onokazu的代码(感谢!),并添加了在XML中具有重复标签的功能,还支持属性,希望有人会发现它有用!

 <?php

function array_to_xml(array $arr, SimpleXMLElement $xml) {
        foreach ($arr as $k => $v) {

            $attrArr = array();
            $kArray = explode(' ',$k);
            $tag = array_shift($kArray);

            if (count($kArray) > 0) {
                foreach($kArray as $attrValue) {
                    $attrArr[] = explode('=',$attrValue);                   
                }
            }

            if (is_array($v)) {
                if (is_numeric($k)) {
                    array_to_xml($v, $xml);
                } else {
                    $child = $xml->addChild($tag);
                    if (isset($attrArr)) {
                        foreach($attrArr as $attrArrV) {
                            $child->addAttribute($attrArrV[0],$attrArrV[1]);
                        }
                    }                   
                    array_to_xml($v, $child);
                }
            } else {
                $child = $xml->addChild($tag, $v);
                if (isset($attrArr)) {
                    foreach($attrArr as $attrArrV) {
                        $child->addAttribute($attrArrV[0],$attrArrV[1]);
                    }
                }
            }               
        }

        return $xml;
    }

        $test_array = array (
          'bla' => 'blub',
          'foo' => 'bar',
          'another_array' => array (
            array('stack' => 'overflow'),
            array('stack' => 'overflow'),
            array('stack' => 'overflow'),
          ),
          'foo attribute1=value1 attribute2=value2' => 'bar',
        );  

        $xml = array_to_xml($test_array, new SimpleXMLElement('<root/>'))->asXML();

        echo "$xml\n";
        $dom = new DOMDocument;
        $dom->preserveWhiteSpace = FALSE;
        $dom->loadXML($xml);
        $dom->formatOutput = TRUE;
        echo $dom->saveXml();
    ?>

可能有助于评论您的更改以使代码更清晰;仍然是不错的补充。 - StormeHawke
这对于我的WP All Export有效。我必须稍微更改is_numeric部分: if (is_numeric($k)) { $i = $k + 1; $child = $xml->addChild("_$i"); array_to_xml($v, $child); } - achiever

5

我需要一个代码,可以将数组内的所有元素作为属性进行处理,并将所有数组作为子元素。

例如:

array (
'row1' => array ('head_element' =>array("prop1"=>"some value","prop2"=>array("empty"))),
"row2"=> array ("stack"=>"overflow","overflow"=>"overflow")
);

我会得到类似这样的东西。
<?xml version="1.0" encoding="utf-8"?>
<someRoot>
  <row1>
    <head_element prop1="some value">
      <prop2 0="empty"/>
    </head_element>
  </row1>
  <row2 stack="overflow" overflow="stack"/>
 </someRoot>

为了实现这个目标,以下是代码,但要非常小心,它是递归的,可能会导致堆栈溢出 :)
function addElements(&$xml,$array)
{
$params=array();
foreach($array as $k=>$v)
{
    if(is_array($v))
        addElements($xml->addChild($k), $v);
    else $xml->addAttribute($k,$v);
}

}
function xml_encode($array)
{
if(!is_array($array))
    trigger_error("Type missmatch xml_encode",E_USER_ERROR);
$xml=new SimpleXMLElement('<?xml version=\'1.0\' encoding=\'utf-8\'?><'.key($array).'/>');
addElements($xml,$array[key($array)]);
return $xml->asXML();
} 

您可能希望添加数组长度的检查,以便某些元素作为数据部分设置,而不是作为属性设置。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接