Results 1 to 2 of 2

Thread: PHP arrays, XML, and crap

  1. PHP arrays, XML, and crap

    I've got this nifty XML parser:
    Code:
    <?
    
    class XmlParser {
    var $params= array();
    var $level = array();
    	
    function XmlParser($input) {
    $xmlp = xml_parser_create();
    xml_parse_into_struct($xmlp, $input, $vals, $index);
    xml_parser_free($xmlp);
    $this->updateMembers($vals, $index);
    }
    	
    // Takes the data and puts it into PHP array  
    function updateMembers($vals, $index) {
    foreach ($vals as $xml_elem) {
      if ($xml_elem['type'] == 'open' ) {
        $this->level[$xml_elem['level']] = strtolower($xml_elem['tag']);
      }
      elseif ($xml_elem['type'] == 'complete') {
        $xml_elem['tag'] = strtolower($xml_elem['tag']);
        $start_level = 1;
        $php_stmt = '$this->params';
        while($start_level < $xml_elem['level']) {
          $php_stmt .= '[$this->level['.$start_level.']]';
          $start_level++;
        }
        $php_stmt .= '[$xml_elem[\'tag\']] = $xml_elem[\'value\'];';
        eval($php_stmt);
    	
    	
      }	
    }
    }
    	
    function getRoot() {
    return $this->level[1];	
    }
    	
    function getData() {
    return $this->params;	
    }
    }
    $xmlp = new XmlParser($xml_response);
    $root = $xmlp->getRoot();
    $data = $xmlp->getData();
    ?>
    It works really well except for one problem- When parsing XML formatted like shown below, it only stores one "item" from the "item" element in the resulting array.

    Code:
    <order>
      <date>2006-09-19</date>
      <cart>
        <items>
          <item>
            <quantity>1</quantity>
            <unit-price currency=\"USD\">80.0</unit-price>
            <item-name>Garbage</item-name>
            <item-description>Smelly</item-description>
          </item>
          <item>
            <quantity>2</quantity>
            <unit-price currency=\"USD\">10.0</unit-price>
            <item-name>Another Item</item-name>
            <item-description>This item Sucks</item-description>
          </item>	  
          <item>
            <quantity>3</quantity>
            <unit-price currency=\"USD\">450.0</unit-price>
            <item-name>nachos</item-name>
            <item-description>Cheesey</item-description>
          </item>	  
        </items>
      </cart>
    </order>
    For instance, when I output echo $data[$root]['cart']['items']['item']['item-name'] . "<br>";, it will only echo "nachos". Presumably because as it loops through the parser, it copies over the "item" element over and over.

    Does anyone have any idea what I can do to make sure all "items" are stored in this array? I cannot alter the XML in any way, so the parser needs to be fixed.

    Thanks in advance for any input.

  2. the best way would be to have it as a numerically indexed array (or, even better, a mix-n-matched array so you can call $data['items'][n]['value'])

    here's a better xml parser that'll pull off what you want it to do, it's pretty much the best xml->array parser I've ever seen that isn't 30 different files from PEAR:

    PHP Code:
    <?php

    class XMLParser {
        var 
    $data;              // Input XML data buffer
        
    var $vals;              // Struct created by xml_parse_into_struct
        
    var $collapse_dups;     // If there is only one tag of a given name, shall we store as scalar or array?
        
    var $index_numeric;     // Index tags by numeric position, not name.  Useful for ordered XML like CallXML.
        
    var $trim_whitespace;   // variable to declare whether or not to keep whitespace inside xml tags

        // Read in XML on object creation.
        // We can take raw XML data, a stream, a filename, or a url.

        
    function XMLParser($data_source$data_source_type='raw'$collapse_dups=0$index_numeric=0$trim_whitespace=0)
        {
            
    $this->collapse_dups   $collapse_dups;
            
    $this->index_numeric   $index_numeric;
            
    $this->trim_whitespace $trim_whitespace;
            
    $this->data '';

            
    // raw input
            
    if ($data_source_type == 'raw')
            {
                
    $this->data $data_source;
            }
            elseif (
    $data_source_type == 'stream') {
                while (!
    feof($data_source))
                {
                    
    $this->data .= fread($data_source1000);
                }
                
    // try filename, then if that fails...
            
    }
            elseif (
    file_exists($data_source))
            {
                
    $this->data implode(''file($data_source));
            }
            
    // try url
            
    else
            {
                
    $fp fopen($data_source,'r');
                while (!
    feof($fp))
                {
                    
    $this->data .= fread($fp1000);
                }
                
    fclose($fp);
            }
        }

        
    // Parse the XML file into a verbose, flat array struct.
        // Then, coerce that into a simple nested array.
        
    function getTree()
        {
            
    $parser xml_parser_create('UTF-8');
            
    xml_parser_set_option($parserXML_OPTION_SKIP_WHITE$this->trim_whitespace);
            
    xml_parser_set_option($parserXML_OPTION_CASE_FOLDING0);
            
    xml_parse_into_struct($parser$this->data$vals$index);
            
    xml_parser_free($parser);

            
    $i = -1;
            return 
    $this->getchildren($vals$i);
        }

        
    // internal function: build a node of the tree
        
    function buildtag($thisvals$vals, &$i$type)
        {
            
    $tag=array();
            if (isset(
    $thisvals['attributes']))
            {
                
    //$tag['attributes'] = $thisvals['attributes'];
                
    $tag $thisvals['attributes'];
            }
            
    // complete tag, just return it for storage in array
            
    if ($type === 'complete')
            {
                
    // check if we're entering an empty value or not
                
    if (!empty($thisvals['value']))
                {
                    
    // not empty, so lets assign $tag to the value element of $thisvals to avoid unnecessary obfuscation
                    // doesn't work on cdata for some reason, oh well not a big deal
                    
    $tag $thisvals['value'];
                }
            }
            
    // open tag, recurse
            
    else
            {
                
    $tag array_merge($tag$this->getchildren($vals$i));
            }
            return 
    $tag;
        }

        
    // internal function: build an nested array representing children
        
    function getchildren($vals, &$i)
        {
            
    $children = array();     // Contains node data

            // Node has CDATA before it's children
            
    if ($i > -&& !empty($vals[$i]['value']))
            {
                
    $children['value'] = $vals[$i]['value'];
            }

            
    // Loop through children, until hit close tag or run out of tags
            
    while (++$i count($vals))
            {
                
    $type $vals[$i]['type'];

                
    // 'cdata':     Node has CDATA after one of it's children
                //              (Add to cdata found before in this case)
                
    if ($type === 'cdata')
                {
                    if (!empty(
    $vals[$i]['value']))
                    {
                        
    $children['value'] .= $vals[$i]['value'];
                    }
                }

                
    // 'complete':  At end of current branch
                // 'open':      Node has children, recurse
                
    elseif ($type === 'complete' || $type === 'open')
                {
                    
    $tag $this->buildtag($vals[$i], $vals$i$type);
                    if (
    $this->index_numeric)
                    {
                        
    $tag['tag'] = $vals[$i]['tag'];
                        
    $children[] = $tag;
                    }
                    else
                    {
                        
    $children[$vals[$i]['tag']][] = $tag;
                    }
                }

                
    // 'close:      End of node, return collected data
                //              Do not increment $i or nodes disappear!
                
    elseif ($type === 'close')
                {
                    break;
                }
            }
            if (
    $this->collapse_dups)
            {
                foreach(
    $children as $key => $value)
                {
                    if (
    is_array($value) && (count($value) == 1) && (!empty($value)))
                    {
                        
    $children[$key] = $value[0];
                    }
                }
                return 
    $children;
            }
        }
    }
    ?>
    edit: I modified a copy of this to use curl to pull data from a external location with cookie support if you want that instead
    Last edited by cka; 26 Oct 2006 at 05:29 AM.

Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  
Games.com logo