Reputation: 127
I'm trying to write a function used to parse XML tags for XML like this:
<Employee>
<salary>40000</salary> <name>John Doe</name> <id>1234</id>
<address>2230 W. Treeline Dr.</address>
<city>Tucson</city>
<state>Arizona</state>
<country>USA</country>
<phone>520-742-2448</phone>
</Employee> <Employee>
<salary>60000</salary> <name>Jane Doe</name> <id>4321</id> </Employee>
<Employee>
<salary>140000</salary>
<state>Michigan</state>
<name>Jack Dough</name>
<id>12345</id>
<city>Dearborn</city>
<country>USA</country>
<phone>303-427-0153</phone>
<address>24437 Princeton</address>
</Employee>
I'm using an std::stack
to keep track of the tags. When an opening tag (e.g. <Employee>
) is encountered, a string representation of the tag name (Employee
) is pushed onto the stack, and, assuming the XML is written correctly, encountering the matching closing tag (e.g. </Employee>
) causes the tag name to be popped off of the stack. However, I have run into an issue where a string is pushed onto the stack so the stack has a size of 1. When the body of the while loop is reached and the body is executed a second time, the stack is emptied. I can't for the life of me figure out what I'm doing wrong. Why does my stack "reset" every time the while
loop reiterates?
static void fromXML(std::istream& in_stream) {
std::stack<std::string> tags;
std::string temp;
std::string tagName;
std::getline(in_stream, temp);
std::istringstream ss(temp);
bool building_employee = true;
int _id;
std::string _name;
std::string _address = "";
std::string _city = "";
std::string _state = "";
std::string _country = "";
std::string _phone = "";
double _salary = 0.0;
while(building_employee && std::getline(ss, temp, '<')) {
std::cout << "Size: " << tags.size() << std::endl; // Prints "0"
std::getline(ss, tagName, '>');
ss.ignore(256, '<');
if (tagName == "") { }
else if ( !tags.empty() && tagName == "/" + tags.top() ) {
std::cout << "Popping " << tags.top() << std::endl;
tags.pop();
}
else if ((tags.empty() && tagName == "Employee") || (!tags.empty() && tagName != "Employee")) {
std::cout << "Pushing " << tagName << std::endl;
tags.push(tagName);
}
else {
std::cerr << "Problem with XML." << std::endl;
}
if (tagName == "/Employee") { building_employee = false; }
else if (tagName == "id") { _id = std::stoi(temp); }
else if (tagName == "name") { _name = temp; }
if (!building_employee) { std::cout << "Employee end." << std::endl; }
if(in_stream.eof()) {
std::cout << "Error: End of file reached without closing <" << temp << ">" << std::endl;
}
std::cout << "Size: " << tags.size() << std::endl; // Prints "1" after pushing one string onto the stack
}
if (temp == "/Employee") { std::cout << "Employee object succesfully created" << std::endl; }
}
Upvotes: 0
Views: 98
Reputation: 38425
Let me be polite for other and do Minimal, Complete, and Verifiable example for them.
#include <iostream>
#include <sstream>
#include <limits>
#include <stack>
static void fromXML(std::istream& in_stream) {
std::stack<std::string> tags;
std::string temp;
std::string tagName;
std::getline(in_stream, temp);
std::istringstream ss(temp);
bool building_employee = true;
int _id;
std::string _name;
std::string _address = "";
std::string _city = "";
std::string _state = "";
std::string _country = "";
std::string _phone = "";
double _salary = 0.0;
while(building_employee && std::getline(ss, temp, '<')) {
std::cout << "Size 1: " << tags.size() << std::endl; // Prints "0"
std::getline(ss, tagName, '>');
ss.ignore(256, '<');
if (tagName == "") { }
else if ( !tags.empty() && tagName == "/" + tags.top() ) {
std::cout << "Popping " << tags.top() << std::endl;
tags.pop();
}
else if ((tags.empty() && tagName == "Employee") || (!tags.empty() && tagName != "Employee")) {
std::cout << "Pushing " << tagName << std::endl;
tags.push(tagName);
}
else {
std::cerr << "Problem with XML." << std::endl;
}
if (tagName == "/Employee") { building_employee = false; }
else if (tagName == "id") { _id = std::stoi(temp); }
else if (tagName == "name") { _name = temp; }
if (!building_employee) { std::cout << "Employee end." << std::endl; }
if(in_stream.eof()) {
std::cout << "Error: End of file reached without closing <" << temp << ">" << std::endl;
}
std::cout << "Size 2: " << tags.size() << std::endl; // Prints "1" after pushing one string onto the stack
}
if (temp == "/Employee") { std::cout << "Employee object succesfully created" << std::endl; }
}
int main()
{
std::istringstream input(
"<Employee>\n"
" <salary>40000</salary> <name>John Doe</name><id>1234</id>\n"
" <address>2230 W. Treeline Dr.</address>\n"
" <city>Tucson</city>\n"
" <state>Arizona</state>\n"
" <country>USA</country>\n"
" <phone>520-742-2448</phone>\n"
"</Employee> <Employee>\n"
" <salary>60000</salary> <name>Jane Doe</name> <id>4321</id> </Employee>\n"
"<Employee>\n"
" <salary>140000</salary>\n"
" <state>Michigan</state>\n"
" <name>Jack Dough</name>\n"
" <id>12345</id\n"
" <city>Dearborn</city>\n"
" <country>USA</country>\n"
" <phone>303-427-0153</phone>\n"
" <address>24437 Princeton</address>\n"
"</Employee>)\n");
fromXML(input);
}
Why does my stack "reset" every time the while loop reiterates?
The stack does not reset, the loop does not iterate. Follow @JiveDadson advice and use a debugger.
The key of the issue consists of two lines
std::getline(in_stream, temp);
std::istringstream ss(temp);
You read one line there and your loop operates on this line only. Since it is the only line, the loop's body is executed once only. Remove those lines and change all corresponding occurrences of ss
in the code to in_stream
.
while(building_employee && std::getline(in_stream, temp, '<')) {
// ...
std::getline(in_stream, tagName, '>');
in_stream.ignore(256, '<');
Upvotes: 1