Page 1 of 1

XML parsing class, variable losing it's value

Posted: Mon May 21, 2007 2:17 pm
by delirium
I've spent hours trying to figure out why a member variable of this parsing class I'm working on is losing it's value. I've showed it to a few people and nobody has been able to figure it out yet. I would greatly appreciate if someone could shed some light on what is going on here....

So, I'm creating a parsing class to read in an XML file for a web surveying script I'm working on. Part of what I need the class to do is assign the question's text to a variable called $qtext which is done by reading in the value between the TEXT tags.

Here is the Parser class I'm using to determine the cause of this problem. The area to focus on is the characterdata function, where I assign the value of $data to member variable $qtext if the tag is TEXT.

Code: Select all

<?php
class Parser {
var $insideq;
var $tag="";
var $qtext="";
var $num=0;


function startElement($parser,$tagname,$attr) {
$this->tag=$tagname;
echo $tagname . "<br>";
echo $this->qtext;
}


function characterData($parser, $data) {
 if ($this->tag=="TEXT") {
	$this->qtext=$data;
	$this->num=1;
 } 
echo $this->qtext;
echo $this->num;

}

function endElement($parser,$tagname) {
 if ($tagname=="SELECTONE") {
	echo $this->qtext;

 }
echo $this->qtext;
echo "\\" . $tagname . "<br>";

}

}

$xml_parser = xml_parser_create();
$parser = new Parser();
xml_set_object($xml_parser,&$parser);
xml_set_element_handler($xml_parser, "startElement", "endElement");
xml_set_character_data_handler($xml_parser, "characterData");
$fp = fopen("mytestxml.php","r")
   or die("Error reading RSS data.");
while ($data = fread($fp, 4096))
   xml_parse($xml_parser, $data, feof($fp))
       or die(sprintf("XML error: %s at line %d",  
           xml_error_string(xml_get_error_code($xml_parser)),  
           xml_get_current_line_number($xml_parser)));
fclose($fp);
xml_parser_free($xml_parser);
?>
Here is the XML code that is being used:

Code: Select all

<SELECTONE>
<TEXT>Testing text</TEXT>
<punch>This is not the text tag</punch>
<PUNCH></PUNCH>
<PUNCH></PUNCH>
</SELECTONE>
And here is the output I get. Notice the value of $qtext does not get printed after the Text tag closes, even though it should. The value of $qtext seems to disappear after that tag is closed, but I have no idea why. The $num variable retains its value. Help!! :(
SELECTONE
0TEXT
Testing text1Testing text\TEXT
1PUNCH
1 \PUNCH
1PUNCH
\PUNCH
1PUNCH
\PUNCH
1 \SELECTONE

Posted: Mon May 21, 2007 3:17 pm
by Christopher
You only assign the datafrom the tag if it is named 'TEXT'. If the tag is 'PUNCH' then no value is assigned.

Code: Select all

if ($this->tag=="TEXT") {
        $this->qtext=$data;

Posted: Mon May 21, 2007 3:39 pm
by delirium
arborint wrote:You only assign the datafrom the tag if it is named 'TEXT'. If the tag is 'PUNCH' then no value is assigned.

Code: Select all

if ($this->tag=="TEXT") {
        $this->qtext=$data;
Right, but why is the value of $qtext disappearing when it goes to a new tag. I want to store that string value from $data into $qtext when it is a TEXT tag, then store a separate string for each PUNCH tag, and then at the end of the SELECTONE tag, it should process all the variables and create a survey question. But the qtext value is gone as soon as the TEXT tag is closed.

I also have more fully developed syntax which also tries to store strings representing data in the punch tags. Those also disappear by the time I need to process everything. As the output seems to indicate, this only is happening with string variables. The numeric value declared in the TEXT tag remains for the duration.

Posted: Mon May 21, 2007 7:17 pm
by Christopher
delirium wrote:Right, but why is the value of $qtext disappearing when it goes to a new tag.
It is probably being overwritten when you return to the SELECTONE block to read the character data between blocks and $this->tag is still set to 'TEXT'.

I rewrote your code a little. It still needs some work but give it a try:

Code: Select all

<?php
class Parser {
	var $data = array();
	var $depth = -1;
	var $filename = '';
	var $errmsg = '';

	function Parser($filename='') {
		$this->filename = $filename;
		$this->xml_parser = xml_parser_create();
		if ($this->xml_parser) {
			xml_set_object($this->xml_parser, &$this);
			xml_set_element_handler($this->xml_parser, "_startElement", "_endElement");
			xml_set_character_data_handler($this->xml_parser, "_characterData");
		} else {
			$this->errmsg = 'Error creating xml_parser';
		}
	}

	function free() {
		xml_parser_free($this->xml_parser);
	}

	function _startElement($parser,$tagname,$attr) {
		$this->tags[++$this->depth] = $tagname;
		$this->data[$tagname] = '';
	}

	function _characterData($parser, $data) {
		$this->data[$this->tags[$this->depth]] .= trim($data);
	}

	function _endElement($parser,$tagname) {
		--$this->depth;
	}

	function read($filename='') {
		if ($this->filename) {
			$this->filename = $this->filename;
		}
		if ($this->xml_parser) {
			$fp = fopen($this->filename, 'r');
			if ($fp) {
				while ($data = fread($fp, 4096)) {
				   if (! xml_parse($this->xml_parser, $data, feof($fp))) {
				       $this->errmsg = sprintf("XML error: %s at line %d", 
				           xml_error_string(xml_get_error_code($this->xml_parser)), 
				           xml_get_current_line_number($this->xml_parser));
						break;
				   }
				}
				fclose($fp);
			} else {
				$this->errmsg = "Error reading RSS data.";
			}
		} else {
			$this->errmsg = "No xml_parser";
		}
	}

}

$parser = new Parser("test.xml");
$parser->read();
echo 'Error: ' . $parser->errmsg . '<br/>';
echo '<pre>' . print_r($parser->data, 1) . '</pre>';

Posted: Tue May 22, 2007 9:21 am
by delirium
Thanks for taking the time to fix up my syntax. This is the first time I've ever tried to write parsing syntax. I'll check it out.

Posted: Tue May 22, 2007 4:16 pm
by delirium
Your syntax is working great. Thank you. :)

I've built on it to suit my needs... Would you mind taking a look at my code and let me know if my form is good or bad and anything I can do to clean it up? I'm not quite sure how to use the depth variable, and I'm not sure if doing so would improve my code.

Here's what I've got.

Code: Select all

class Parser {
        //parse variables
        var $data = array();
        var $depth = -1;
        var $filename = '';
        var $errmsg = '';

        //structural variables
        var $TestSurvey;    //Survey object
        var $qname;        //stores question name
        var $Punches;       //an array to hold punches
        var $numPunches=0; //number of punches in a question
        var $punchVal=1;      //stores the value of the next punch value to use


        function Parser($filename='') {
                $this->filename = $filename;
                $this->xml_parser = xml_parser_create();
                if ($this->xml_parser) {
                        xml_set_object($this->xml_parser, &$this);
                        xml_set_element_handler($this->xml_parser, "_startElement", "_endElement");
                        xml_set_character_data_handler($this->xml_parser, "_characterData");

                        $this->TestSurvey=new Survey();
                } else {
                        $this->errmsg = 'Error creating xml_parser';
                }
        }

        function free() {
                xml_parser_free($this->xml_parser);
        }

        function _startElement($parser,$tagname,$attr) {
                $this->tags[++$this->depth] = $tagname;

                if ($tagname=="SELECTONE") {    //if start of SELECTONE tag, initialize new question
                        $this->initQ();
                        if ($attr["NAME"])       //set question name through the attribute
                          $this->qname=$attr["NAME"];
                        else
                          $this->qname="UNNAMED";
                }

                if ($tagname=="PUNCH" && isset($this->data["PUNCH"])) {                        //if punch data already exists, add new data to array
                        $this->Punches[$this->numPunches]->ptext=$this->data["PUNCH"];
                        $this->Punches[$this->numPunches]->pvalue=$this->punchVal;
                        $this->numPunches++;
                        $this->punchVal++;
                        if ($attr["VAL"])                   //if a value is set, override incremental value
                           $this->punchVal=$attr["VAL"];
                }

                $this->data[$tagname] = '';
        }

        function _characterData($parser, $data) {
                $this->data[$this->tags[$this->depth]] .= trim($data);
        }

        function _endElement($parser,$tagname) {
                --$this->depth;
                if ($tagname=='SELECTONE') {       //if end of SELECTONE tag, process the question
                        $this->processQ();
                }
        }

        //PROCESS QUESTION
        function initQ() {
        //Initialize new question
                $this->data["PUNCH"]=NULL;    //reset punches
                $this->numPunches=0;
                $this->punchVal=1;
        }

        function processQ() {
        //Add question to the array
                $this->Punches[$this->numPunches]->pvalue=$this->punchVal;
                $this->Punches[$this->numPunches]->ptext=$this->data["PUNCH"];
                $this->numPunches++;

                $this->TestSurvey->addQuestion($this->qname,$this->data["TEXT"],$this->Punches,$this->numPunches);
        }

        //READ FILE
        function read($filename='') {
                if ($this->filename) {
                        $this->filename = $this->filename;
                }
                if ($this->xml_parser) {
                        $fp = fopen($this->filename, 'r');
                        if ($fp) {
                                while ($data = fread($fp, 4096)) {
                                   if (! xml_parse($this->xml_parser, $data, feof($fp))) {
                                       $this->errmsg = sprintf("XML error: %s at line %d",
                                           xml_error_string(xml_get_error_code($this->xml_parser)),
                                           xml_get_current_line_number($this->xml_parser));
                                                break;
                                   }
                                }
                                fclose($fp);
                        } else {
                                $this->errmsg = "Error reading RSS data.";
                        }
                } else {
                        $this->errmsg = "No xml_parser";
                }
        }

}
Here's the test XML I'm using:

Code: Select all

<SURVEY>
<SELECTONE NAME="sex">
<TEXT>What is your sex?</TEXT>
<punch>Male</punch>
<PUNCH>Female</PUNCH>
<PUNCH VAL="9">Don't know/Refused</PUNCH>
</SELECTONE>

<SELECTONE NAME="race">
<TEXT>What is your race?</TEXT>
<punch>White</punch>
<PUNCH>Black</PUNCH>
<punch>Hispanic</punch>
<PUNCH VAL="99">Don't know/Refused</PUNCH>
</SELECTONE>
</SURVEY>