首页 > 解决方案 > PHP:如何以最有效的方式将 XML 解析为数组?

问题描述

我需要更快的方法来将 XML 解析为数组(没有空值)。

到目前为止,我正在使用 Array2XML(由 Lalit Patel 编写)库将 XML 解析为数组,但它是脚本的瓶颈。我正在寻求加快速度,发现速度快了大约 15 倍:

class SimpleXmlDecoder
{

    public function decode(string $xml): array
    {
        try {
            $decoded = json_decode(json_encode(
                simplexml_load_string($xml, "SimpleXMLElement", LIBXML_NOCDATA)
            ),TRUE);

            if (empty($decoded)) {
                return [];
            }

            return self::mapEmptyArraysElementsToEmptyString($decoded);
        } catch (\Exception $exception) {
            return [];
        }
    }

    private static function mapEmptyArraysElementsToEmptyString($array): array
    {
        return array_map(
            static function($value) {
                if (!is_array($value)) {
                    return $value;
                }

                if (empty($value)) {
                    return '';
                }

                return self::mapEmptyArraysElementsToEmptyString($value);
            },
            $array
        );
    }

}

现在已经足够了,但将来可能会成为瓶颈。你知道更快的方法吗?

@Edit 每个 XML 的大小:100kB-1MB 需要从所有具有名称和值的非空元素中返回值。

标签: phparraysxml

解决方案


我只是快速拼凑了xmlparser下面的类,它使用RecursiveDOMIterator该类来处理 xml 文件。我不知道这是否会比您的原始代码更快-在本地处理文件时似乎相当轻快-它设法在 2.4 秒内处理了一个非常复杂的 8Mb xml 文件,但压缩了较小的文件。我很想知道它的比较表现如何

<?php

    class RecursiveDOMIterator implements RecursiveIterator {
        /*
            https://github.com/salathe/spl-examples/wiki/RecursiveDOMIterator
        */
        private $index;
        private $list;

        public function __construct(DOMNode $domNode){
            $this->index = 0;
            $this->list = $domNode->childNodes;
        }
        public function current(){
            return $this->list->item($this->index);
        }
        public function getChildren(){
            return new self( $this->current() );
        }
        public function hasChildren(){
            return $this->current()->hasChildNodes();
        }
        public function key(){
            return $this->index;
        }
        public function next(){
            $this->index++;
        }
        public function rewind(){
            $this->index = 0;
        }
        public function valid(){
            return $this->index < $this->list->length;
        }
    }//end class


    class xmlparser{
        private static $instance=false;
        private $start;
        private $dom;

        private function __construct( $xml ){
            $this->start=microtime( true );
            libxml_use_internal_errors( true );
            $this->dom=new DOMDocument;
            $this->dom->validateOnParse=true;
            $this->dom->recover=true;
            $this->dom->strictErrorChecking=true;

            if( is_file( $xml ) && file_exists( $xml ) ) $this->dom->load( $xml );
            else $this->dom->loadXML( $xml );

            libxml_clear_errors();
        }

        private function __clone(){}
        public function __wakeup(){}
        public static function initialise( $xml ){
            if( !self::$instance ) self::$instance=new xmlparser( $xml );
            return self::$instance;
        }

        public function parse(){
            $itr = new RecursiveIteratorIterator( new RecursiveDOMIterator( $this->dom ), RecursiveIteratorIterator::SELF_FIRST );
            $tmp=[];
            foreach( $itr as $node) {
                if( $node->nodeType === XML_ELEMENT_NODE ) {

                    $tag=$node->tagName;
                    $value=$node->nodeValue;

                    if( !empty( $value ) ){
                        $element=[
                            'tag'   =>  $tag,
                            'value' =>  $value
                        ];
                        if( $node->hasAttributes() ){
                            $attributes=[];
                            foreach( $node->attributes as $index => $attr ){
                                $attributes[ $attr->nodeName ]=$attr->nodeValue;
                            }
                            $element['attributes']=$attributes;
                        }
                        $tmp[]=$element;
                    }
                }
            }
            $this->duration=microtime( true ) - $this->start;
            return $tmp;
        }
        public function __get( $name ){
            return $this->$name;
        }
    }//end class



    $file = 'bbc_rss.xml';
    $obj = xmlparser::initialise( $file );
    $data = $obj->parse();
    $time = $obj->duration;
    $size = round( $obj->filesize/pow( 1024, 2 ),2 );



    printf( "Time: %s\nSize:%sMb", $time, $size );

?>

推荐阅读