Search code examples
phpxmlsimplexml

PHP retrieving all Node names and node values from a nested XML string


Trying to find a way to read an XML file and get all the nodes. Some of the XML files have 1 step depth, others 2, others more. Is there anyway to get all the nodes for all children without knowing their name? e.g. i wrote this piece of code but this works only for 2 steps depth

foreach ($xml->children() as $node) {
        if ($node->children()->count()>0) {
            foreach ($node->children() as $cnode){
                echo $cnode->getName()."<br>";
            }
        }
        echo $node->getName()."<br>";
    }
<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope" >
   <soap:Header/>
   <soap:Body>
      <multitrans xmlns="http://www.xxxx.xx/">
         <authentication>
            <username>xxxxxx</username>
            <password>xxxxxx</password>
            <clientid>xxxxxx</clientid>
         </authentication>
         <requests>
            <trans_request>
               <TransType tc="100"/>
               <company tc="500"/>
               <product tc="auto"/>
               <inception>
                  <p_year>2017</p_year>
                  <p_month>5</p_month>
                  <p_day>15</p_day>
               </inception>
               <p_number>0</p_number>
               <attributes>
                              <att val="0" name="SVCsynchronouscall" />
                              <att val="2" name="value1" />
                              <att val="2017-5-15" name="Date" />
                              <att val="0" name="value2" />
                              <att val="0" name="value3" />
                              <att val="0" name="value4" />
               </attributes>
<warnings>

</warnings>
            </trans_request>
         </requests>
      </multitrans>
   </soap:Body>
</soap:Envelope>

Solution

  • Here we are using DOMDocument and DOMXPath to find the innerHTML of soap:Body. And then we treating innerHTML in simplexml_load_string and converting it into array using json_encode and json_decode, and finally we are using array_walk_recursive to get all values.

    Try another code snippet here Demo for simple xml not-nested

    Try this code snippet here Demo for nested xml

    $string = '<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope" >
       <soap:Header/>
       <soap:Body>
          <multitrans xmlns="http://www.xxxx.xx/">
             <authentication>
                <username>xxxxxx</username>
                <password>xxxxxx</password>
                <clientid>xxxxxx</clientid>
             </authentication>
             <requests>
                <trans_request>
                   <TransType tc="100"/>
                   <company tc="500"/>
                   <product tc="auto"/>
                   <inception>
                      <p_year>2017</p_year>
                      <p_month>5</p_month>
                      <p_day>15</p_day>
                   </inception>
                   <p_number>0</p_number>
                   <attributes>
                      <att val="0" name="SVCsynchronouscall" />
                      <att val="2" name="value1" />
                      <att val="2017-5-15" name="Date" />
                      <att val="0" name="value2" />
                      <att val="0" name="value3" />
                      <att val="0" name="value4" />
                   </attributes>
                <warnings>
                </warnings>
                </trans_request>
             </requests>
          </multitrans>
       </soap:Body>
    </soap:Envelope>';
     $finalResult = array();
    $domObject = new DOMDocument();
    $domObject->loadXML($string);
    
    $domXPATH = new DOMXPath($domObject);
    $results = $domXPATH->query("//soap:Body/*");
    
    foreach($results as $result)
    {
        if($result->childNodes->length==1 && $result->childNodes->item(0) instanceof  DOMText)
        {
            $finalResult[$result->tagName] = $result->textContent;
        }
        else
        {
            $array = json_decode(json_encode(simplexml_load_string($result->ownerDocument->saveXML($result))), true);
            array_walk_recursive($array, function($value,$key) use(&$finalResult)
            {
                if (!empty($value))
                {
                    if(isset($finalResult[$key])&& is_string($finalResult[$key]) )
                    {
                        $temp=$finalResult[$key];
                        $finalResult[$key] = array($temp,$value);
                    }
                    elseif(isset($finalResult[$key]) && is_array($finalResult[$key]) && count($finalResult[$key])>0)
                    {
                        $finalResult[$key]=  array_merge(array($value),$finalResult[$key]);
                    }
                    else
                    {
                        $finalResult[$key]=$value;
                    }
                }
            });        
        }
    
    }
    print_r(array_filter($finalResult));
    

    output:

     Array
    (
        [username] => xxxxxx
        [password] => xxxxxx
        [clientid] => xxxxxx
        [tc] => Array
            (
                [0] => auto
                [1] => 100
                [2] => 500
            )
    
        [p_year] => 2017
        [p_month] => 5
        [p_day] => 15
        [name] => Array
            (
                [0] => value4
                [1] => value3
                [2] => value2
                [3] => Date
                [4] => SVCsynchronouscall
                [5] => value1
            )
    
        [val] => Array
            (
                [0] => 2
                [1] => 2017-5-15
            )
    )