简体   繁体   中英

How to convert XML into array in PHP?

I want to convert below XML to PHP array. Any suggestions on how I can do this?

<aaaa Version="1.0">
   <bbb>
     <cccc>
       <dddd Id="id:pass" />
       <eeee name="hearaman" age="24" />
     </cccc>
   </bbb>
</aaaa>

easy!

$xml = simplexml_load_string($xmlstring, "SimpleXMLElement", LIBXML_NOCDATA);
$json = json_encode($xml);
$array = json_decode($json,TRUE);

Another option is the SimpleXML extension (I believe it comes standard with most php installs.)

http://php.net/manual/en/book.simplexml.php

The syntax looks something like this for your example

$xml = new SimpleXMLElement($xmlString);
echo $xml->bbb->cccc->dddd['Id'];
echo $xml->bbb->cccc->eeee['name'];
// or...........
foreach ($xml->bbb->cccc as $element) {
  foreach($element as $key => $val) {
   echo "{$key}: {$val}";
  }
}

Converting an XML string ( $buffer ) into a simplified array ignoring attributes and grouping child-elements with the same names:

function XML2Array(SimpleXMLElement $parent)
{
    $array = array();

    foreach ($parent as $name => $element) {
        ($node = & $array[$name])
            && (1 === count($node) ? $node = array($node) : 1)
            && $node = & $node[];

        $node = $element->count() ? XML2Array($element) : trim($element);
    }

    return $array;
}

$xml   = simplexml_load_string($buffer);
$array = XML2Array($xml);
$array = array($xml->getName() => $array);

Result:

Array
(
    [aaaa] => Array
        (
            [bbb] => Array
                (
                    [cccc] => Array
                        (
                            [dddd] => 
                            [eeee] => 
                        )

                )

        )

)

If you also want to have the attributes, they are available via JSON encoding/decoding of SimpleXMLElement. This is often the most easy quick'n'dirty solution:

$xml   = simplexml_load_string($buffer);
$array = json_decode(json_encode((array) $xml), true);
$array = array($xml->getName() => $array);

Result:

Array
(
    [aaaa] => Array
        (
            [@attributes] => Array
                (
                    [Version] => 1.0
                )

            [bbb] => Array
                (
                    [cccc] => Array
                        (
                            [dddd] => Array
                                (
                                    [@attributes] => Array
                                        (
                                            [Id] => id:pass
                                        )

                                )

                            [eeee] => Array
                                (
                                    [@attributes] => Array
                                        (
                                            [name] => hearaman
                                            [age] => 24
                                        )

                                )

                        )

                )

        )

)

Take note that all these methods only work in the namespace of the XML document.

$array = json_decode(json_encode((array)simplexml_load_string($xml)),true);

The method used in the accepted answer drop attributes when encountering child elements with only a text node. For example:

$xml = '<container><element attribute="123">abcd</element></container>';
print_r(json_decode(json_encode(simplexml_load_string($xml, "SimpleXMLElement", LIBXML_NOCDATA)),1));

Array
(
    [element] => abcd
)

My solution (and I wish I could give credit here because I'm sure I adapted this from something):

function XMLtoArray($xml) {
    $previous_value = libxml_use_internal_errors(true);
    $dom = new DOMDocument('1.0', 'UTF-8');
    $dom->preserveWhiteSpace = false; 
    $dom->loadXml($xml);
    libxml_use_internal_errors($previous_value);
    if (libxml_get_errors()) {
        return [];
    }
    return DOMtoArray($dom);
}

function DOMtoArray($root) {
    $result = array();

    if ($root->hasAttributes()) {
        $attrs = $root->attributes;
        foreach ($attrs as $attr) {
            $result['@attributes'][$attr->name] = $attr->value;
        }
    }

    if ($root->hasChildNodes()) {
        $children = $root->childNodes;
        if ($children->length == 1) {
            $child = $children->item(0);
            if (in_array($child->nodeType,[XML_TEXT_NODE,XML_CDATA_SECTION_NODE])) {
                $result['_value'] = $child->nodeValue;
                return count($result) == 1
                    ? $result['_value']
                    : $result;
            }

        }
        $groups = array();
        foreach ($children as $child) {
            if (!isset($result[$child->nodeName])) {
                $result[$child->nodeName] = DOMtoArray($child);
            } else {
                if (!isset($groups[$child->nodeName])) {
                    $result[$child->nodeName] = array($result[$child->nodeName]);
                    $groups[$child->nodeName] = 1;
                }
                $result[$child->nodeName][] = DOMtoArray($child);
            }
        }
    }
    return $result;
}

$xml = '
    <aaaa Version="1.0">
       <bbb>
         <cccc>
           <dddd id="123" />
           <eeee name="john" age="24" />
           <ffff type="employee">Supervisor</ffff>
         </cccc>
       </bbb>
    </aaaa>
';
print_r(XMLtoArray($xml));

Array
(
    [aaaa] => Array
        (
            [@attributes] => Array
                (
                    [Version] => 1.0
                )

            [bbb] => Array
                (
                    [cccc] => Array
                        (
                            [dddd] => Array
                                (
                                    [@attributes] => Array
                                        (
                                            [id] => 123
                                        )

                                )

                            [eeee] => Array
                                (
                                    [@attributes] => Array
                                        (
                                            [name] => john
                                            [age] => 24
                                        )

                                )

                            [ffff] => Array
                                (
                                    [@attributes] => Array
                                        (
                                            [type] => employee
                                        )

                                    [_value] => Supervisor
                                )

                        )

                )

        )

)

See https://github.com/gaarf/XML-string-to-PHP-array/blob/master/xmlstr_to_array.php

<?php
/**
  * convert xml string to php array - useful to get a serializable value
  *
  * @param string $xmlstr
  * @return array
  *
  * @author Adrien aka Gaarf & contributors
  * @see http://gaarf.info/2009/08/13/xml-string-to-php-array/
*/
function xmlstr_to_array($xmlstr) {
  $doc = new DOMDocument();
  $doc->loadXML($xmlstr);
  $root = $doc->documentElement;
  $output = domnode_to_array($root);
  $output['@root'] = $root->tagName;
  return $output;
}
function domnode_to_array($node) {
  $output = array();
  switch ($node->nodeType) {
    case XML_CDATA_SECTION_NODE:
    case XML_TEXT_NODE:
      $output = trim($node->textContent);
    break;
    case XML_ELEMENT_NODE:
      for ($i=0, $m=$node->childNodes->length; $i<$m; $i++) {
        $child = $node->childNodes->item($i);
        $v = domnode_to_array($child);
        if(isset($child->tagName)) {
          $t = $child->tagName;
          if(!isset($output[$t])) {
            $output[$t] = array();
          }
          $output[$t][] = $v;
        }
        elseif($v || $v === '0') {
          $output = (string) $v;
        }
      }
      if($node->attributes->length && !is_array($output)) { //Has attributes but isn't an array
        $output = array('@content'=>$output); //Change output into an array.
      }
      if(is_array($output)) {
        if($node->attributes->length) {
          $a = array();
          foreach($node->attributes as $attrName => $attrNode) {
            $a[$attrName] = (string) $attrNode->value;
          }
          $output['@attributes'] = $a;
        }
        foreach ($output as $t => $v) {
          if(is_array($v) && count($v)==1 && $t!='@attributes') {
            $output[$t] = $v[0];
          }
        }
      }
    break;
  }
  return $output;
}

Surprised no one mentioned xml_parse_into_struct :

$simple = "<para><note>simple note</note></para>";
$p = xml_parser_create();
xml_parse_into_struct($p, $simple, $vals, $index);
xml_parser_free($p);
echo "Index array\n";
print_r($index);
echo "\nVals array\n";
print_r($vals);

XML To Array

More Details Visit https://github.com/sapankumarmohanty/lamp/blob/master/Crate-XML-2-Array

//Convert XML to array and SOAP XML to array

function xml2array($contents, $get_attributes = 1, $priority = 'tag')
    {
        if (!$contents) return array();
        if (!function_exists('xml_parser_create')) {
            // print "'xml_parser_create()' function not found!";
            return array();
        }
        // Get the XML parser of PHP - PHP must have this module for the parser to work
        $parser = xml_parser_create('');
        xml_parser_set_option($parser, XML_OPTION_TARGET_ENCODING, "UTF-8"); // http://minutillo.com/steve/weblog/2004/6/17/php-xml-and-character-encodings-a-tale-of-sadness-rage-and-data-loss
        xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, 0);
        xml_parser_set_option($parser, XML_OPTION_SKIP_WHITE, 1);
        xml_parse_into_struct($parser, trim($contents) , $xml_values);
        xml_parser_free($parser);
        if (!$xml_values) return; //Hmm...
        // Initializations
        $xml_array = array();
        $parents = array();
        $opened_tags = array();
        $arr = array();
        $current = & $xml_array; //Refference
        // Go through the tags.
        $repeated_tag_index = array(); //Multiple tags with same name will be turned into an array
        foreach($xml_values as $data) {
            unset($attributes, $value); //Remove existing values, or there will be trouble
            // This command will extract these variables into the foreach scope
            // tag(string), type(string), level(int), attributes(array).
            extract($data); //We could use the array by itself, but this cooler.
            $result = array();
            $attributes_data = array();
            if (isset($value)) {
                if ($priority == 'tag') $result = $value;
                else $result['value'] = $value; //Put the value in a assoc array if we are in the 'Attribute' mode
            }
            // Set the attributes too.
            if (isset($attributes) and $get_attributes) {
                foreach($attributes as $attr => $val) {                                   
                                    if ( $attr == 'ResStatus' ) {
                                        $current[$attr][] = $val;
                                    }
                    if ($priority == 'tag') $attributes_data[$attr] = $val;
                    else $result['attr'][$attr] = $val; //Set all the attributes in a array called 'attr'
                }
            }
            // See tag status and do the needed.
                        //echo"<br/> Type:".$type;
            if ($type == "open") { //The starting of the tag '<tag>'
                $parent[$level - 1] = & $current;
                if (!is_array($current) or (!in_array($tag, array_keys($current)))) { //Insert New tag
                    $current[$tag] = $result;
                    if ($attributes_data) $current[$tag . '_attr'] = $attributes_data;
                                        //print_r($current[$tag . '_attr']);
                    $repeated_tag_index[$tag . '_' . $level] = 1;
                    $current = & $current[$tag];
                }
                else { //There was another element with the same tag name
                    if (isset($current[$tag][0])) { //If there is a 0th element it is already an array
                        $current[$tag][$repeated_tag_index[$tag . '_' . $level]] = $result;
                        $repeated_tag_index[$tag . '_' . $level]++;
                    }
                    else { //This section will make the value an array if multiple tags with the same name appear together
                        $current[$tag] = array(
                            $current[$tag],
                            $result
                        ); //This will combine the existing item and the new item together to make an array
                        $repeated_tag_index[$tag . '_' . $level] = 2;
                        if (isset($current[$tag . '_attr'])) { //The attribute of the last(0th) tag must be moved as well
                            $current[$tag]['0_attr'] = $current[$tag . '_attr'];
                            unset($current[$tag . '_attr']);
                        }
                    }
                    $last_item_index = $repeated_tag_index[$tag . '_' . $level] - 1;
                    $current = & $current[$tag][$last_item_index];
                }
            }
            elseif ($type == "complete") { //Tags that ends in 1 line '<tag />'
                // See if the key is already taken.
                if (!isset($current[$tag])) { //New Key
                    $current[$tag] = $result;
                    $repeated_tag_index[$tag . '_' . $level] = 1;
                    if ($priority == 'tag' and $attributes_data) $current[$tag . '_attr'] = $attributes_data;
                }
                else { //If taken, put all things inside a list(array)
                    if (isset($current[$tag][0]) and is_array($current[$tag])) { //If it is already an array...
                        // ...push the new element into that array.
                        $current[$tag][$repeated_tag_index[$tag . '_' . $level]] = $result;
                        if ($priority == 'tag' and $get_attributes and $attributes_data) {
                            $current[$tag][$repeated_tag_index[$tag . '_' . $level] . '_attr'] = $attributes_data;
                        }
                        $repeated_tag_index[$tag . '_' . $level]++;
                    }
                    else { //If it is not an array...
                        $current[$tag] = array(
                            $current[$tag],
                            $result
                        ); //...Make it an array using using the existing value and the new value
                        $repeated_tag_index[$tag . '_' . $level] = 1;
                        if ($priority == 'tag' and $get_attributes) {
                            if (isset($current[$tag . '_attr'])) { //The attribute of the last(0th) tag must be moved as well
                                $current[$tag]['0_attr'] = $current[$tag . '_attr'];
                                unset($current[$tag . '_attr']);
                            }
                            if ($attributes_data) {
                                $current[$tag][$repeated_tag_index[$tag . '_' . $level] . '_attr'] = $attributes_data;
                            }
                        }
                        $repeated_tag_index[$tag . '_' . $level]++; //0 and 1 index is already taken
                    }
                }
            }
            elseif ($type == 'close') { //End of tag '</tag>'
                $current = & $parent[$level - 1];
            }
        }
        return ($xml_array);
    }
    
    // Let's call the this above function xml2array
    
    xml2array($xmlContent, $get_attributes = 3, $priority = 'tag'); // it will work 100% if not ping me @skype: sapan.mohannty
    
//  Enjoy coding

I liked this question and some answers was helpful to me, but i need to convert the xml to one domination array, so i will post my solution maybe someone need it later:

<?php
$xml = json_decode(json_encode((array)simplexml_load_string($xml)),1);
$finalItem = getChild($xml);
var_dump($finalItem);

function getChild($xml, $finalItem = []){
    foreach($xml as $key=>$value){
        if(!is_array($value)){
            $finalItem[$key] = $value;
        }else{
            $finalItem = getChild($value, $finalItem);
        }
    }
    return $finalItem;
}
?>  

Two lines of code ( https://www.php.net/manual/en/book.simplexml.php#113485 )

$xml = new SimpleXMLElement("<your><xml><string>ok</string></xml></your>");
$array = (array)$xml;

I know I'm 1 billion years late, but I had the same problem as you and needed a more complex solution, so here is a function ( xml_decode() ) I made to convert SimpleXMLElements into PHP arrays without losing attributes and with arguments for a more customizable use.

The val() function is meant for you to customize how element values shall be treated - in case you want to transform <elem>true</elem> into true instead of "true" for example.

Disclaimer: I know it is easier to just use the PHP SimpleXML extension, but I needed to transform lots of XML files into JSON files for a big change in a project of mine. Also, the question is about how to transform XML to PHP arrays, not how to use XML in PHP.

<?php

function val($input) {
    return strval($input);
}

/**
 * Transform an SimpleXMLElement into an associative array.
 * 
 * @param SimpleXMLElement $xml The XML element to be decoded.
 * 
 * @param bool $attributes_key If the element attributes should be grouped into a single element.
 * 
 * Example: <elem foo="true" bar="false" />
 * 
 * If true, xml_decode() will output
 * array("attributes" => array("foo" => "true", "bar" => "false"))
 * 
 * If false, xml_decode() will output
 * array("foo" => "true", "bar" => "false")
 * 
 * @param bool $reduce If unecessary keys created due to XML structure should be eliminated.
 * 
 * Example: <fruits><fruit>apple</fruit><fruit>banana</fruit></fruits>
 * 
 * If true, xml_decode() will output the element as
 * array("fruits" => array(0 => "apple", 1 => "banana"))
 * 
 * If false, xml_decode() will output the element as
 * array("fruits" => array("fruit" => array(0 => "apple", 1 => "banana")))
 * 
 * @param array $always_array List of which childs should be treated aways as an array.
 * 
 * Example: <fruits><fruit>apple</fruit></fruits>
 * 
 * If array("fruit") is passed as $aways_array, xml_decode() will output the element as
 * array("fruits" => array("fruit" => array(0 => "apple")))
 * 
 * If not, xml_decode() will output the element as
 * array("fruits" => array("fruit" => "apple"))
 * 
 * @param array $value_keys List of custom element's value names. This argument is only
 * used when values need to passed as elements because of attributes or other reasons.
 * 
 * The default value key name is "value".
 * 
 * Example: <fruits><fruit id="123">apple</fruit></fruits>
 * 
 * If array("fruit" => "name) is passed as $value_keys, xml_decode() will output the element as
 * array("fruits" => array("fruit" => array("attributes" => array("id" => "123"), "name" => "apple")))
 * 
 * If not, xml_decode() will output the element as
 * array("fruits" => array("fruit" => array("attributes" => array("id" => "123"), "value" => "apple")))
 */
function xml_decode(SimpleXMLElement $xml, bool $attributes_key = true, bool $reduce = true,
    array $always_array = array(), array $value_keys = array()): string|array {

    // Inicialize the array.
    $arr = array();

    // XML tag name.
    $xml_name = $xml->getName();

    // Turn attributes into elements.
    foreach ($xml->attributes() as $key => $value) {
        // Use a key for attributes if $attributes_key argument is true.
        if ($attributes_key) {
            $arr['attributes'][val($key)] = val($value);
        } else {
            $arr[val($key)] = val($value);
        }
    }

    // Count children.
    $children_count = $xml->children()->count();

    // No children? Value will be text.
    if ($children_count == 0) {

        // If attributes were found and turned into elements
        // the value shall be an element.
        if (count($arr) > 0) {
            // If attributes were found previosly.
            $key = $value_keys[$xml_name] ?? $value_keys['*'] ?? "value";
            $arr[$key] = val($xml);
        // Else, no need for an array.
        } else {
            $arr = val($xml);
        }

    // Children? Loop continues.
    } else {

        // Defines if there are unecessary array keys - due to the XML structure - to be cut.
        // Example: <fruits><fruit /><fruit /><fruits />
        // could be turned into arr['fruits'][0] and arr['fruits'][1] instead of
        // arr['fruits']['fruit'][0] and arr['fruits']['fruit'][1] for a
        // cleaner organization.
        $children_names = array();
        foreach ($xml->children() as $child) {
            $child_name = $child->getName();
            in_array($child_name, $children_names) or $children_names[] = $child_name;
        }
        $reducible = empty($arr) && count($children_names) === 1;

        foreach ($xml->children() as $child) {

            // Child's name shall be the element key.
            $name = $child->getName();
            
            // Children with the same name will be turned into a list.
            // Example: $arr['repeating-child'][...] = $value;
            if ($xml->$name->count() > 1 || in_array($name, $always_array)) {

                // Reduction, if possible and requested by the $reduce argument.
                if ($reduce && $reducible) {
                    $arr[] = xml_decode($child, $attributes_key, $reduce, $always_array, $value_keys);
                } else {
                    $arr[$name][] = xml_decode($child, $attributes_key, $reduce, $always_array, $value_keys);
                }

            // Normal children will be normally decoded.
            // Example: $arr['no-repeating-child] = $value;
            } else {
                
                $arr[$name] = xml_decode($child, $attributes_key, $reduce, $always_array, $value_keys);

            }
        }
    }

    return $arr;

}

Resuming all the documentation and comments, the function transforms attributes and elements values into simple array elements and uses a loop with itself to process elements which contain children.

The arguments allow you to:

  • Group attributes into separate keys;
  • Cut unecessary keys generated due to the XML structure conversion (Example: fruits->fruit to $arr['fruits']['fruit'][n] );
  • Set elements which should aways be treated as lists (because sometimes it will have only one child element but you still need it to be a list);
  • Set a name for array element keys which will represent an XML element text value - which will be needed when attributes are converted to array elements.

Usage example with your XML elements (I think you already solved it after 11 years, but I'm answering it, so...):

test.xml

<test>
    <aaaa Version="1.0">
        <bbb>
            <cccc>
                <dddd Id="id:pass" />
                <eeee name="hearaman" age="24" />
            </cccc>
        </bbb>
    </aaaa>
</test>

PHP

$xml = simplexml_load_file("test.xml");
$decode = xml_decode($xml);
echo "<pre>" . print_r($decode,true) . "</pre>";

Output

Array
(
    [aaaa] => Array
        (
            [attributes] => Array
                (
                    [Version] => 1.0
                )

            [bbb] => Array
                (
                    [cccc] => Array
                        (
                            [dddd] => Array
                                (
                                    [attributes] => Array
                                        (
                                            [Id] => id:pass
                                        )

                                    [value] => 
                                )

                            [eeee] => Array
                                (
                                    [attributes] => Array
                                        (
                                            [name] => hearaman
                                            [age] => 24
                                        )

                                    [value] => 
                                )

                        )

                )

        )

)

 /* Creating an XML file (Optional): Create an XML file which need to convert into the array. test.xml */ <aaaa Version="1.0"> <bbb> <cccc> <dddd Id="id:pass" /> <eeee name="hearaman" age="24" /> </cccc> </bbb> </aaaa> <?php // xml file path $path = "text.xml"; // set your according path for dynamic. // Read entire file into string $xmlfile = file_get_contents($path); // Convert xml string into an object $new = simplexml_load_string($xmlfile); // Convert into json $con = json_encode($new); // Convert into associative array $newArr = json_decode($con, true); print_r($newArr); ?> Output: Result of XML conversion to PHP Array [ 'aaaa' => [ 'bbb' => [ 'cccc' => [ 'dddd' => [ '@value' => '', '@attributes' => [ 'Id' => 'id:pass', ], ], 'eeee' => [ '@value' => '', '@attributes' => [ 'name' => 'hearaman', 'age' => '24', ], ], ], ], '@attributes' => [ 'Version' => '1.0', ], ], ]

If you are getting parse error for unexpected string or getting empty array then use this:

$xml= preg_replace("/(<\/?)(\w+):([^>]*>)/", "$1$2$3", $xml); // adding this worked for me
$array = json_decode(json_encode((array)simplexml_load_string($xml)),true);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM