C++ Use a stack to parse an input XML file, check the XML for validity, and to u
ID: 664258 • Letter: C
Question
C++
Use a stack to parse an input XML file, check the XML for validity, and to use the data to create data payload instances from the input XML. The driver program is given in the code on the website. You must im- plement a class XMLParser class that includes the methods invoked by the driver. • First read the DTD (Document Type Definition) file to create an set of the legal XML tags for this file. This will be done in the XMLParser class. • Read the XML line by line. • Determine if the incoming token is an XML tag or real data. • If the token is an open tag, verify that it is legal in the DTD, and then push it onto the stack. • If the token is just data, push it onto the stack. • If the token is a close tag, verify it is legal in the DTD, and then pop the stack until you get the first open token and compare the token against the close to ensure that they match. • If a tag is not legal, then flag bad parsing with a message. • If the stack popping to verify proper nesting shows the nesting to be incorrect, you should throw an error to that effect. As you are reading tokens: after having determined what kind of input line you have, you should log that line to the output file so you know you have read things correctly.
Explanation / Answer
XMLParser.h
#ifndef XMLPARSER_H_
#define XMLPARSER_H_
#include <string>
class XMLParser
{
public:
XMLParser();
virtual ~XMLParser();
bool setSource(std::string xmlText);
bool setSource(std::ifstream* xmlFile);
std::string getTagValue(std::string xmlTag, unsigned int nth = 1);
int getTagCount(std::string xmlTag);
bool validateXML(std::string& errorMessage);
void hierarchyToTerminal();
private :
std::string xmlContents;
void outputNode(std::string nodeName, int hierLevel);
};
#endif /* XMLPARSER_H_ */
XMLParser.cpp
#include "XMLParser.h"
#include <string>
#include <fstream>
#include <stack>
XMLParser::XMLParser()
{
}
XMLParser::~XMLParser()
{
}
bool XMLParser::setSource(std::string xmlText)
{
xmlContents = xmlText;
if (xmlContents.length() == 0)
{
return false;
}
return true;
}
bool XMLParser::setSource(std::ifstream* xmlFile)
{
xmlContents = "";
std::string textLine = "";
if (xmlFile->is_open())
{
while (xmlFile->good())
{
getline(*xmlFile, textLine);
xmlContents += textLine;
}
xmlFile->close();
}
else
{
return false; // error loading file
}
if (xmlContents.length() == 0)
{
return false;
}
return true;
}
/*
* This function grabs the contents of the 'nth' XML tag.
* If no value given for 'nth', the first occurrence will be returned.
* Returns an empty string if not found, or if no source has been set.
*
*/
std::string XMLParser::getTagValue(std::string xmlTag, unsigned int nth)
{
std::string tagValue = "";
std::string openTag = "<" + xmlTag + ">";
if (xmlContents.length() == 0)
{
return "";
}
unsigned int cursorLocA = 0;
unsigned int cursorLocB = 0;
for (int thisTag = nth; thisTag > 0; thisTag--)
{
cursorLocA = xmlContents.find(&openTag[0], cursorLocA, (2 + xmlTag.length()));
if (thisTag != 1)
{
cursorLocA++;
}
}
if (cursorLocA == std::string::npos)
{
// failed to find closing tag, reached EOF
return "";
}
cursorLocA = cursorLocA + (2 + xmlTag.length()); // got start of data
// Find closing tag, or another opening tag position...
cursorLocB = xmlContents.find("<", cursorLocA, 1);
try
{
tagValue = xmlContents.substr(cursorLocA, cursorLocB - cursorLocA);
}
catch (std::exception exc)
{
tagValue = "";
}
return tagValue;
}
int XMLParser::getTagCount(std::string xmlTag)
{
std::string tagToCount = "<" + xmlTag + ">";
unsigned int cursorPos = 0;
int tagCount = 0;
do
{
cursorPos = xmlContents.find(&tagToCount[0], cursorPos, (2 + xmlTag.length()));
if (cursorPos == std::string::npos)
{
break;
}
cursorPos++;
tagCount++;
}
while (true);
return tagCount;
}
/*
* return 0 if error, 1 otherwise.
*/
bool XMLParser::validateXML(std::string& errorMessage)
{
bool error = false;
if (xmlContents.length() == 0)
{
return 0;
}
std::stack<std::string> tagStack;
unsigned int cursorLocA = 0;
unsigned int cursorLocB = 0;
std::string currentTag = "";
do
{
try
{
cursorLocA = xmlContents.find("<", cursorLocB, 1);
cursorLocB = xmlContents.find(">", cursorLocA, 1);
if (cursorLocB == std::string::npos)
{
// end of file.
break;
}
currentTag = xmlContents.substr(cursorLocA, (cursorLocB - cursorLocA) + 1);
// First, carry on if tag is DTD or self closing
if ((currentTag.substr(0, 2) == "<?") || (currentTag.substr(currentTag.length() - 2, 2) == "/>"))
{
continue;
}
if ((currentTag.substr(0, 1) == "<") && (currentTag.substr(0, 2) != "</"))
{
// open tag found
tagStack.push(currentTag);
}
if (currentTag.substr(0, 2) == "</")
{
// closing tag found
if (currentTag.substr(2, currentTag.length() - 2) == tagStack.top().substr(1,
tagStack.top().length() - 1))
{
// okay, they match. Pop off opening tag
tagStack.pop();
}
else
{
errorMessage = "Expected closing tag for " + tagStack.top() + ", but found " + currentTag
+ ".";
error = true;
}
}
}
catch (std::exception exc)
{
errorMessage = "An unknown error occurred.";
error = true;
}
}
while (!error);
if (tagStack.size() == 0)
{
return true; // all okay
}
if (errorMessage.length() == 0) // check no existing errors....
{
errorMessage = tagStack.top() + " lacks closing tag.";
}
for (unsigned int emptyStack = 0; emptyStack < tagStack.size(); emptyStack++)
{
tagStack.pop();
}
return false; // almost okay, stack not empty though so a closing tag missing?
}
void XMLParser::hierarchyToTerminal()
{
int level = 0;
bool error = false;
if (xmlContents.length() == 0)
{
return;
}
std::stack<std::string> tagStack;
unsigned int cursorLocA = 0;
unsigned int cursorLocB = 0;
std::string currentTag = "";
do
{
try
{
cursorLocA = xmlContents.find("<", cursorLocB, 1);
cursorLocB = xmlContents.find(">", cursorLocA, 1);
if (cursorLocB == std::string::npos)
{
// end of file.
break;
}
currentTag = xmlContents.substr(cursorLocA, (cursorLocB - cursorLocA) + 1);
// First, carry on if tag is DTD or self closing
if ((currentTag.substr(0, 2) == "<?") || (currentTag.substr(currentTag.length() - 2, 2) == "/>"))
{
outputNode(currentTag, level);
continue;
}
if ((currentTag.substr(0, 1) == "<") && (currentTag.substr(0, 2) != "</"))
{
// open tag found
tagStack.push(currentTag);
outputNode(currentTag, level);
level++;
}
if (currentTag.substr(0, 2) == "</")
{
// closing tag found
if (currentTag.substr(2, currentTag.length() - 2) == tagStack.top().substr(1,
tagStack.top().length() - 1))
{
// okay, they match. Pop off opening tag
tagStack.pop();
level--;
outputNode(currentTag, level);
}
else
{
printf("Expected closing tag for %s, but found %s.", &tagStack.top()[0], ¤tTag[0]);
error = true;
}
}
}
catch (std::exception exc)
{
printf("An unknown error occurred.");
error = true;
}
}
while (!error);
if (!error) // check no existing errors....
{
printf("%s lacks closing tag.", &tagStack.top()[0]);
}
for (unsigned int emptyStack = 0; emptyStack < tagStack.size(); emptyStack++)
{
tagStack.pop();
}
return; // almost okay, stack not empty though so a closing tag missing?
}
void XMLParser::outputNode(std::string nodeName, int hierLevel)
{
for (int indent = 0; indent < 4 * hierLevel; indent++)
{
printf(" ");
}
printf("%s", &nodeName[0]);
printf(" ");
}
Related Questions
drjack9650@gmail.com
Navigate
Integrity-first tutoring: explanations and feedback only — we do not complete graded work. Learn more.