在PHP中将关联数组转换为XML

10

我想知道PHP(或某个普遍可用的PHP库)是否有能够将关联数组转换为XML文档的函数。

我已经进行了相当多的搜索,但只找到了一些不能输出有效XML的函数。我相信我测试的这个数组是正确构造的,因为它可以正常用于生成JSON文档使用json_encode。然而,它相当大,并且嵌套在四个级别上,这可能解释了为什么我尝试过的函数失败了。

最终,我将编写代码来自己生成XML,但肯定有更快的方法。


这是重复的问题:https://dev59.com/6XM_5IYBdhLWcg3wZSI6 - tres.14159
7个回答

7

我意识到我有点晚了,但我一直在解决这个问题 - 我找到的教程 几乎 能够解决它(但是单元测试时不能)。

经过很多挫折和研究,我得出了以下解决方案:

将XML转换为关联数组:

来自http://www.php.net/manual/en/simplexml.examples-basic.php

json_decode( json_encode( simplexml_load_string( $string ) ), TRUE );

关联数组转换为XML

注意:

  • 不处理XML属性。
  • 还可以处理带有数字索引的嵌套数组(这在XML中是无效的!)

来源:http://www.devexp.eu/2009/04/11/php-domdocument-convert-array-to-xml/

/// Converts an array to XML
/// - http://www.devexp.eu/2009/04/11/php-domdocument-convert-array-to-xml/
/// @param  <array> $array  The associative array you want to convert; nested numeric indices are OK!
function   getXml( array $array )  {

    $array2XmlConverter  = new XmlDomConstructor('1.0', 'utf-8');
    $array2XmlConverter->xmlStandalone   = TRUE;
    $array2XmlConverter->formatOutput    = TRUE;

    try {
        $array2XmlConverter->fromMixed( $array );
        $array2XmlConverter->normalizeDocument ();
        $xml    = $array2XmlConverter->saveXML();
//        echo "\n\n-----vvv start returned xml vvv-----\n";
//        print_r( $xml );
//        echo "\n------^^^ end returned xml ^^^----\n"
        return  $xml;
    }
    catch( Exception $ex )  {
//        echo "\n\n-----vvv Rut-roh Raggy! vvv-----\n";
//        print_r( $ex->getCode() );     echo "\n";
//        print_r( $->getMessage() );
//        var_dump( $ex );
//        echo "\n------^^^ end Rut-roh Raggy! ^^^----\n"
        return  $ex;
    }
}

......这里是用于$array2XmlConverter对象的类:

/**
 * Extends the DOMDocument to implement personal (utility) methods.
 * - From: http://www.devexp.eu/2009/04/11/php-domdocument-convert-array-to-xml/
 * - `parent::` See http://www.php.net/manual/en/class.domdocument.php
 *
 * @throws   DOMException   http://www.php.net/manual/en/class.domexception.php
 *
 * @author Toni Van de Voorde
 */
class   XmlDomConstructor   extends DOMDocument {

    /**
     * Constructs elements and texts from an array or string.
     * The array can contain an element's name in the index part
     * and an element's text in the value part.
     *
     * It can also creates an xml with the same element tagName on the same
     * level.
     *
     * ex:
        \verbatim
             <nodes>
                <node>text</node>
                <node>
                    <field>hello</field>
                    <field>world</field>
                </node>
             </nodes>
        \verbatim
     *
     *
     * Array should then look like:
        \verbatim
             array(
                "nodes" => array(
                    "node" => array(
                        0 => "text",
                        1 => array(
                            "field" => array (
                                0 => "hello",
                                1 => "world",
                            ),
                        ),
                    ),
                ),
             );
        \endverbatim
     *
     * @param mixed $mixed An array or string.
     *
     * @param DOMElement[optional] $domElement Then element
     * from where the array will be construct to.
     *
     */
    public  function    fromMixed($mixed, DOMElement $domElement = null) {

        $domElement = is_null($domElement) ? $this : $domElement;

        if (is_array($mixed)) {
            foreach( $mixed as $index => $mixedElement ) {

                if ( is_int($index) ) {
                    if ( $index == 0 ) {
                        $node = $domElement;
                    } 
                    else {
                        $node = $this->createElement($domElement->tagName);
                        $domElement->parentNode->appendChild($node);
                    }
                }
                else {
                    $node = $this->createElement($index);
                    $domElement->appendChild($node);
                }

                $this->fromMixed($mixedElement, $node);
            }
        } 
        else {
            $domElement->appendChild($this->createTextNode($mixed));
        }
    }
} // end of class

5

没有这样的内置功能。编写此功能并不是问题。

肯定有更快的方法来做到这一点

您如何在数组中表示属性?我可以假设键是标签,值是该标签的内容。

基本的PHP数组 -> JSON完美运行,因为这些结构几乎相同。


2
JSON对象和PHP数组在本质上非常相似;XML则完全不同,主要是因为(a)属性与值和(b)具有相同名称的多个节点(即,数值索引数组,但是如何选择名称?);就个人而言,我认为你无法在项目/应用程序范围之上制定一种适合所有情况的解决方案。 - Dereleased
1
好的,你说得对。并没有银弹。 所以我最终使用DOMDocument构建了自己的XML。想一想,这种方法也更有趣。 - Epicurus

2

调用

// $data = array(...);
$dataTransformator = new DataTransformator();
$domDocument = $dataTransformator->data2domDocument($data);
$xml = $domDocument->saveXML();

DataTransformator

class DataTransformator {

    /**
     * Converts the $data to a \DOMDocument.
     * @param array $data
     * @param string $rootElementName
     * @param string $defaultElementName
     * @see MyNamespace\Dom\DataTransformator#data2domNode(...)
     * @return Ambigous <DOMDocument>
     */
    public function data2domDocument(array $data, $rootElementName = 'data', $defaultElementName = 'item') {
        return $this->data2domNode($data, $rootElementName, null, $defaultElementName);
    }

    /**
     * Converts the $data to a \DOMNode.
     * If the $elementContent is a string,
     * a DOMNode with a nested shallow DOMElement
     * will be (created if the argument $node is null and) returned.
     * If the $elementContent is an array,
     * the function will applied on every its element recursively and
     * a DOMNode with a nested DOMElements
     * will be (created if the argument $node is null and) returned.
     * The end result is always a DOMDocument object.
     * The casue is, that a \DOMElement object
     * "is read only. It may be appended to a document,
     * but additional nodes may not be appended to this node
     * until the node is associated with a document."
     * See {@link http://php.net/manual/en/domelement.construct.php here}).
     * 
     * @param Ambigous <string, mixed> $elementName Used as element tagname. If it's not a string $defaultElementName is used instead.
     * @param Ambigous <string, array> $elementContent
     * @param Ambigous <\DOMDocument, NULL, \DOMElement> $parentNode The parent node is
     *  either a \DOMDocument (by the method calls from outside of the method)
     *  or a \DOMElement or NULL (by the calls from inside).
     *  Once again: For the calls from outside of the method the argument MUST be either a \DOMDocument object or NULL.
     * @param string $defaultElementName If the key of the array element is a string, it determines the DOM element name / tagname.
     *  For numeric indexes the $defaultElementName is used.
     * @return \DOMDocument
     */
    protected function data2domNode($elementContent, $elementName, \DOMNode $parentNode = null, $defaultElementName = 'item') {
        $parentNode = is_null($parentNode) ? new \DOMDocument('1.0', 'utf-8') : $parentNode;
        $name = is_string($elementName) ? $elementName : $defaultElementName;
        if (!is_array($elementContent)) {
            $content = htmlspecialchars($elementContent);
            $element = new \DOMElement($name, $content);
            $parentNode->appendChild($element);
        } else {
            $element = new \DOMElement($name);
            $parentNode->appendChild($element);
            foreach ($elementContent as $key => $value) {
                $elementChild = $this->data2domNode($value, $key, $element);
                $parentNode->appendChild($elementChild);
            }
        }
        return $parentNode;
    }
}

1
function combArrToXML($arrC=array(), $root="root", $element="element"){
  $doc = new DOMDocument();
  $doc->formatOutput = true;

  $r = $doc->createElement( $root );
  $doc->appendChild( $r );

  $b = $doc->createElement( $element );
  foreach( $arrC as  $key => $val)
  {
    $$key = $doc->createElement( $key );
    $$key->appendChild(
      $doc->createTextNode( $val )
    );
    $b->appendChild( $$key );
    $r->appendChild( $b );
  }

  return $doc->saveXML();
}

例子:

$b=array("testa"=>"testb", "testc"=>"testd");
combArrToXML($b, "root", "element");

输出:

<?xml version="1.0"?>
<root>
  <element>
    <testa>testb</testa>
    <testc>testd</testc>
  </element>
</root>

1

仍然...它不是内建函数。我认为@manbearpig在谈论“开箱即用”的函数...用C++编写以提高性能...当然我可能错了。 - Nemoden
DOMDocument类是内置的 - 只是示例实现不是。相反,DOMDocument是一个标准扩展,很可能已经安装在大多数当前的PHP实例中。 - 65Fbef05

0
当然有更快的方法来做这件事。
如果您安装了PEAR,则可以使用{{link1:看一下}} XML_Seralizer。它还处于测试版阶段,所以您将不得不使用。
 pear install XML_Serializer-beta

安装


0

我需要一个能够转换非关联子数组和需要使用CDATA(<>&)进行转义的内容的解决方案。由于我找不到任何合适的解决方案,所以我基于SimpleXML实现了自己的解决方案,应该相当快。

https://github.com/traeger/SimplestXML(此解决方案支持(关联)数组 => XML 和 XML =>(关联)数组转换,但不支持属性)。我希望这能帮助到某些人。


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