XML to Array

Mit dieser Klasse wird das XML in ein Array umgewandelt.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
<?php
/**
 * XmlToArray: A class to convert XML to array in PHP
 * It returns the array which can be converted back to XML using the Array2XML script
 * It takes an XML string or a DOMDocument object as an input.
 *
 * See Array2XML: http://www.lalit.org/lab/convert-php-array-to-xml-with-attributes
 *
 * Author : Lalit Patel
 * Website: http://www.lalit.org/lab/convert-xml-to-array-in-php-XmlToArray
 * License: Apache License 2.0
 *          http://www.apache.org/licenses/LICENSE-2.0
 *
 * Usage:
 *          $array = XmlToArray::createArray($xml);
 */


if(!class_exists('XmlToArray')) {
    class XmlToArray {
        private static $xml = null;
        private static $encoding = 'UTF-8';

        /**
         * Initialize the root XML node [optional]
         *
         * @param string $version
         * @param string $encoding
         * @param bool $format_output
         */

        public static function init($version = '1.0', $encoding = 'UTF-8', $format_output = true) {
            self::$xml = new DOMDocument($version, $encoding);
            self::$xml->formatOutput = $format_output;
            self::$encoding = $encoding;
        } // END public static function init($version = '1.0', $encoding = 'UTF-8', $format_output = true)

        /**
         * Convert an XML to Array
         *
         * @param string $input_xml
         * @throws Exception
         * @return Ambigous DOMDocument <Ambigous, string, multitype:multitype: string multitype:string  Ambigous >
         */

        public static function &createArray($input_xml) {
            $input_xml = self::strip_comment($input_xml);
            $xml = self::getXMLRoot();

            if(is_string($input_xml)) {
                $parsed = $xml->loadXML($input_xml);

                if(!$parsed) {
                    throw new Exception('[XmlToArray] Error parsing the XML string.');
                } // END if(!$parsed)
            } else {
                if(get_class($input_xml) != 'DOMDocument') {
                    throw new Exception('[XmlToArray] The input XML object should be of type: DOMDocument.');
                } // END if(get_class($input_xml) != 'DOMDocument')

                $xml = self::$xml = $input_xml;
            } // END if(is_string($input_xml))

            $array[$xml->documentElement->tagName] = self::convert($xml->documentElement);

            self::$xml = null; // clear the xml node in the class for 2nd time use.

            return $array;
        } // END public static function &createArray($input_xml)

        /**
         * Convert an Array to XML
         *
         * @param string $node XML as a string or as an object of DOMDocument
         * @return Ambigous <string, multitype:multitype: string multitype:string  unknown >
         */

        private static function &convert($node) {
            $output = array();

            switch($node->nodeType) {
                case XML_CDATA_SECTION_NODE:
                    $output['@cdata'] = trim($node->textContent);
                    break;

                case XML_TEXT_NODE:
                    $output = trim($node->textContent);
                    break;

                case XML_ELEMENT_NODE:
                    // for each child node, call the covert function recursively
                    for($i = 0, $m = $node->childNodes->length; $i < $m; $i++) {
                        $child = $node->childNodes->item($i);
                        $v = self::convert($child);

                        if(isset($child->tagName)) {
                            $t = $child->tagName;

                            // assume more nodes of same kind are coming
                            if(!isset($output[$t])) {
                                $output[$t] = array();
                            } // END if(!isset($output[$t]))

                            $output[$t][] = $v;
                        } else {
                            //check if it is not an empty text node
                            if($v !== '') {
                                $output = $v;
                            } // END if($v !== '')
                        } // END if(isset($child->tagName))
                    } // END for($i = 0, $m = $node->childNodes->length; $i < $m; $i++)

                    if(is_array($output)) {
                        // if only one node of its kind, assign it directly instead if array($value);
                        foreach($output as $t => $v) {
                            if(is_array($v) && count($v) == 1) {
                                $output[$t] = $v[0];
                            } // END if(is_array($v) && count($v) == 1)
                        } // END foreach($output as $t => $v)

                        if(empty($output)) {
                            //for empty nodes
                            $output = '';
                        } // END if(empty($output))
                    } // END if(is_array($output))

                    // loop through the attributes and collect them
                    if($node->attributes->length) {
                        $a = array();
                        foreach($node->attributes as $attrName => $attrNode) {
                            $a[$attrName] = (string) $attrNode->value;
                        } // END foreach($node->attributes as $attrName => $attrNode)

                        // if its an leaf node, store the value in @value instead of directly storing it.
                        if(!is_array($output)) {
                            $output = array(
                                '@value' => $output
                            );
                        } // END if(!is_array($output))

                        $output['@attributes'] = $a;
                    } // END if($node->attributes->length)

                    break;
            } // END switch($node->nodeType)

            return $output;
        } // END private static function &convert($node)

        /**
         * Get the root XML node, if there isn't one, create it.
         *
         * @return NULL
         */

        private static function getXMLRoot() {
            if(empty(self::$xml)) {
                self::init();
            } // END if(empty(self::$xml))

            return self::$xml;
        } // END private static function getXMLRoot()

        /**
         * Strip the comments out of our XML
         *
         * @param string $input_xml
         * @return string
         */

        private static function strip_comment($input_xml) {
            return preg_replace('/<!--(?:.(?<!--))*-->/', '', $input_xml);
        } // END private static function strip_comment($input_xml)
    } // END class XmlToArray
} // END if(!class_exists('XmlToArray'))

$xml = '<?xml version="1.0" encoding="UTF-8"?>
<movies type="documentary">
    <movie>
        <title>PHP: Behind the Parser</title>
        <characters>
            <character>
                <name>Ms. Coder</name>
                <actor>Onlivia Actora</actor>
            </character>
            <character>
                <name>Mr. Coder</name>
                <actor>El ActÓr</actor>
            </character>
        </characters>
        <plot><![CDATA[So, this language. It\'s like, a programming language. Or is it a scripting language? All is revealed in this thrilling horror spoof of a documentary.]]></plot>
        <great-lines>
            <line>PHP solves all my web problems</line>
        </great-lines>
        <rating type="thumbs">7</rating>
        <rating type="stars">5</rating>
    </movie>
</movies>'
;

$array = XmlToArray::createArray($xml);

echo '<pre>';
print_r($array);

Array to XML

Und hier wieder zurück in ein XML

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
<?php
/**
 * ArrayToXml: A class to convert array in PHP to XML
 * It also takes into account attributes names unlike SimpleXML in PHP
 * It returns the XML in form of DOMDocument class for further manipulation.
 * It throws exception if the tag name or attribute name has illegal chars.
 *
 * Author : Lalit Patel
 * Website: http://www.lalit.org/lab/convert-php-array-to-xml-with-attributes
 * License: Apache License 2.0
 *          http://www.apache.org/licenses/LICENSE-2.0
 *
 * Usage:
 *          $xml = ArrayToXml::createXML('root_node_name', $php_array);
 *          echo $xml->saveXML();
 */

if(!class_exists('ArrayToXml')) {
    class ArrayToXml {
        private static $xml = null;
        private static $encoding = 'UTF-8';

        /**
         * Initialize the root XML node [optional]
         *
         * @param string $version
         * @param string $encoding
         * @param bool $format_output
         */

        public static function init($version = '1.0', $encoding = 'UTF-8', $format_output = true) {
            self::$xml = new DomDocument($version, $encoding);
            self::$xml->formatOutput = $format_output;
            self::$encoding = $encoding;
        } // END public static function init($version = '1.0', $encoding = 'UTF-8', $format_output = true)

        /**
         * Convert an Array to XML
         *
         * @param string $node_name name of the root node to be converted
         * @param array $arr aray to be converterd
         * @return DomDocument
         */

        public static function &createXML($node_name, $arr = array()) {
            $xml = self::getXMLRoot();
            $xml->appendChild(self::convert($node_name, $arr));

            self::$xml = null; // clear the xml node in the class for 2nd time use.

            return $xml;
        } // END public static function &createXML($node_name, $arr = array())

        /**
         * Convert an Array to XML
         *
         * @param string $node_name name of the root node to be converted
         * @param array $arr aray to be converterd
         * @throws Exception
         * @return DOMNode
         */

        private static function &convert($node_name, $arr = array()) {
            //print_arr($node_name);
            $xml = self::getXMLRoot();
            $node = $xml->createElement($node_name);

            if(is_array($arr)) {
                // get the attributes first.;
                if(isset($arr['@attributes'])) {
                    foreach($arr['@attributes'] as $key => $value) {
                        if(!self::isValidTagName($key)) {
                            throw new Exception('[ArrayToXml] Illegal character in attribute name. attribute: ' . $key . ' in node: ' . $node_name);
                        } // END if(!self::isValidTagName($key))

                        $node->setAttribute($key, self::bool2str($value));
                    } // END foreach($arr['@attributes'] as $key => $value)

                    unset($arr['@attributes']); //remove the key from the array once done.
                } // END if(isset($arr['@attributes']))

                // check if it has a value stored in @value, if yes store the value and return
                // else check if its directly stored as string
                if(isset($arr['@value'])) {
                    $node->appendChild($xml->createTextNode(self::bool2str($arr['@value'])));

                    unset($arr['@value']); //remove the key from the array once done.

                    //return from recursion, as a note with value cannot have child nodes.
                    return $node;
                } else if(isset($arr['@cdata'])) {
                    $node->appendChild($xml->createCDATASection(self::bool2str($arr['@cdata'])));

                    unset($arr['@cdata']); //remove the key from the array once done.

                    //return from recursion, as a note with cdata cannot have child nodes.
                    return $node;
                } // END if(isset($arr['@value']))
            } // END if(is_array($arr))

            //create subnodes using recursion
            if(is_array($arr)) {
                // recurse to get the node for that key
                foreach($arr as $key => $value) {
                    if(!self::isValidTagName($key)) {
                        throw new Exception('[ArrayToXml] Illegal character in tag name. tag: ' . $key . ' in node: ' . $node_name);
                    } // END if(!self::isValidTagName($key))

                    if(is_array($value) && is_numeric(key($value))) {
                        // MORE THAN ONE NODE OF ITS KIND;
                        // if the new array is numeric index, means it is array of nodes of the same kind
                        // it should follow the parent key name
                        foreach($value as $k => $v) {
                            $node->appendChild(self::convert($key, $v));
                        } // END foreach($value as $k => $v)
                    } else {
                        // ONLY ONE NODE OF ITS KIND
                        $node->appendChild(self::convert($key, $value));
                    } // END if(is_array($value) && is_numeric(key($value)))

                    unset($arr[$key]); //remove the key from the array once done.
                } // END foreach($arr as $key => $value)
            } // END if(is_array($arr))

            // after we are done with all the keys in the array (if it is one)
            // we check if it has any text value, if yes, append it.
            if(!is_array($arr)) {
                $node->appendChild($xml->createTextNode(self::bool2str($arr)));
            } // END if(!is_array($arr))

            return $node;
        } // END private static function &convert($node_name, $arr = array())

        /**
         * Get the root XML node, if there isn't one, create it.
         *
         * @return string
         */

        private static function getXMLRoot() {
            if(empty(self::$xml)) {
                self::init();
            } // END if(empty(self::$xml))

            return self::$xml;
        } // END private static function getXMLRoot()

        /**
         * Get string representation of boolean value
         *
         * @param bool $v
         * @return Ambigous <string, unknown>
         */

        private static function bool2str($v) {
            //convert boolean to text value.
            $v = ($v === true) ? 'true' : $v;
            $v = ($v === false) ? 'false' : $v;

            return $v;
        } // END private static function bool2str($v)

        /**
         * Check if the tag name or attribute name contains illegal characters Ref:
         * http://www.w3.org/TR/xml/#sec-common-syn
         *
         * @param string $tag
         * @return boolean
         */

        private static function isValidTagName($tag) {
            $pattern = '/^[a-z_]+[a-z0-9\:\-\.\_]*[^:]*$/i';

            return preg_match($pattern, $tag, $matches) && $matches[0] == $tag;
        } // END private static function isValidTagName($tag)
    } // END class ArrayToXml
} // END if(!class_exists('ArrayToXml'))

$array = array(
    '@attributes' => array(
        'type' => 'fiction'
    ),
    'book' => array(
        array(
            '@attributes' => array(
                'author' => 'George Orwell'
            ),
            'title' => '1984'
        ),
        array(
            '@attributes' => array(
                'author' => 'Isaac Asimov'
            ),
            'title' => array(
                '@cdata'=>'Foundation'
            ),
            'price' => '$15.61'
        ),
        array(
            '@attributes' => array(
                'author' => 'Robert A. Heinlein'
            ),
            'title' =>  array(
                '@cdata'=>'Stranger in a Strange Land'
            ),
            'price' => array(
                '@attributes' => array(
                    'discount' => '10%'
                ),
                '@value' => '$18.00'
            )
        )
    )
);

$xml = ArrayToXml::createXML('books', $array);
echo htmlentities($xml->saveXML());

Credits

Wie ich schon eingangs erwähnte, habe ich das Rad hier nicht neu erfunden, sondern zwei vorgefertigte Lösungen hergenommen. Die Variante XML to Array habe ich noch um eine Methode erweitert, welche mir eventuelle Kommentare erst mal aus dem Array rauswirft, da diese nur stören bei der Umwandling. „Erfunden“ hat die beiden Klassen Lalit Patel der diese auch veröffentlicht hat.

XML to Array by Lalit Patel: http://www.lalit.org/lab/convert-xml-to-array-in-php-xml2array/
Array to XML by Lalit Patel: http://www.lalit.org/lab/convert-php-array-to-xml-with-attributes/
Mein Dank gilt daher diesem Herren, er hat mir ne Menge Zeit erspart :-)

Artikel / Seite weiterempfehlen

5 Meinungen zu “(PHP) Einmal XML to Array und zurück bitte

  1. Guten Tag,

    ich habe die class File XML2Array ausprobiert,
    und wenn die XML File intern im Script liegt ist auch alles
    wunderbar.

    wie kann ich eine externe xml file abarbeiten lassen.
    $xml = simplexml_load_file(‚path/to/file‘);

    funtkioniert so nicht.

    Grüße

  2. Hallo, danke für den Code, aber was mache ich falsch?
    Wenn ich den oberen ‚XML to Array‘-Abschnitt als PHP-Datei speichere (XAMPP) ihn dann im Browser aufrufe, erhalte ich folgenden Fehler:
    Warning: DOMDocument::loadXML(): Input is not proper UTF-8, indicate encoding ! Bytes: 0xD3 0x72 0x3C 0x2F in Entity, line: 12 in C:\xampp\htdocs\xmltoarray.php on line 48

    Fatal error: Uncaught exception ‚Exception‘ with message ‚[XmlToArray] Error parsing the XML string.‘ in C:\xampp\htdocs\xmltoarray.php:51 Stack trace: #0 C:\xampp\htdocs\xmltoarray.php(194): XmlToArray::createArray(‚<?xml version="…') #1 {main} thrown in C:\xampp\htdocs\xmltoarray.php on line 51

Schreibe einen Kommentar

Ihre Email-Adresse wird nicht veröffentlicht. Pflichtfelder sind durch * markiert.

Sie können folgende HTML-Tags benutzen: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>