I'm trying to parse an XML file using TinyXML in order to build a General Tree. I'm attempting to do this recursively. The problem is, I'm getting a segmentation fault whenever I do it.
Here is the snippet:
void buildTree() {
// Loading XML file and getting rootNode
string filename = "generalTree.xml";
TiXmlDocument doc(filename);
bool loadOkay = doc.LoadFile();
if (!loadOkay) {
cout << "Could not load file " << filename << endl;
cout << "Error='" << doc.ErrorDesc() <<"'. Exiting.\n";
}
TiXmlNode* generalTreeNode = doc.FirstChild("GeneralTree");
TiXmlNode* rootNode = generalTreeNode->FirstChild();
int key = stoi(rootNode->ToElement()->Attribute("key"));
Type data = rootNode->ToElement()->GetText();
root = new TreeNode<Type>("General", key, data);
// Populating the rest of the tree via recursive function
recFunction(rootNode);
}
Here is recFunction:
void recFunction(TiXmlNode *node) {
if(node->FirstChildElement() == NULL) {
cout << "First child element is null" << endl;
} else {
int key = stoi(node->ToElement()->Attribute("key"));
Type data = node->ToElement()->GetText();
TreeNode<Type> *treeNode = new TreeNode<Type>("General", key, data);
cout << "Right BEFORE recursive activates" << endl;
return recFunction(node->FirstChild());
}
cout << "After recursiveness done" << endl;
// After recursiveness is finished
while(node->NextSibling() != NULL) {
if(!node) {
cout << "Node is null, breaking" << endl;
break;
}
// Converting XML node to TreeNode
cout << "DOING NODE TO ELEMENT" << endl;
cout << node->ToText()->Value() << endl;
cout << "Node is of type: " << typeid(node).name() << endl;
cout << node->ToElement()->Attribute("key") << endl;
cout << "DONE WITH NODE TO ELEMENT" << endl;
int key = stoi(node->ToElement()->Attribute("key"));
cout << "Key 1 is: " << key << endl;
Type data = node->ToElement()->GetText();
cout << "Data 1 is: " << data << endl;
TreeNode<Type> *prev = new TreeNode<Type>("General", key, data);
int key2 = stoi(node->NextSibling()->ToElement()->Attribute("key"));
Type data2 = node->ToElement()->GetText();
TreeNode<Type> *cur = new TreeNode<Type>("General", key2, data2);
// Create linked list of siblings
prev->setSibling(cur);
node = node->NextSibling();
}
cout << "End of while loop reached" << endl;
}
And here is the XML file:
<?xml version="1.0"?>
<GeneralTree>
<Node key="1"> Genres
<Node key="2">Thriller</Node>
<Node key="3">Action</Node>
<Node key="4">Romance
<Node key="7">A Walk To Remember</Node>
<Node key="8">The Notebook</Node>
<Node key="9">Safe Haven</Node>
</Node>
<Node key="5">Anime
<Node key="9">Full Metal Alchemist</Node>
<Node key="10">Pokemon 2000: The Movie</Node>
</Node>
</Node>
<Node key="12">Genre Sister</Node>
</GeneralTree>
Now, I have isolated the problem to the following lines in recFunction:
cout << "DOING NODE TO ELEMENT" << endl;
cout << node->ToText()->Value() << endl;
cout << "Node is of type: " << typeid(node).name() << endl;
cout << node->ToElement()->Attribute("key") << endl;
cout << "DONE WITH NODE TO ELEMENT" << endl;
So I'm assuming node turns null at some point in the recursive function. The problem is I have looked over this again and again and I can't seem to figure out why. The checks in place should prevent node from becoming null.
Here is the output I receive (including the print statements so you can see where it's happening).
Right BEFORE recursive activates
First child element is null
After recursiveness done
DOING NODE TO ELEMENT
Genres
Node is of type: P9TiXmlNode
Thanks in advance!
EDIT: here is what happens when I run gdb and use a breakpoint at
cout << node->ToText()->Value() << endl;
and cout << node->ToElement()->Attribute("key") << endl;
Right BEFORE recursive activates
First child element is null
After recursiveness done
DOING NODE TO ELEMENT
Breakpoint 1, GeneralTree<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >::recFunction (this=0x100300040, node=0x1003005c0)
at ./GeneralTree.h:70
70 cout << node->ToText()->Value() << endl;
(gdb) c
Continuing.
Genres
Node is of type: P9TiXmlNode
Breakpoint 2, GeneralTree<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >::recFunction (this=0x100300040, node=0x1003005c0)
at ./GeneralTree.h:72
72 cout << node->ToElement()->Attribute("key") << endl;
(gdb) c
Continuing.
Program received signal SIGSEGV, Segmentation fault.
TiXmlAttributeSet::Find (this=0x60, name=0x100018ede "key") at tinyxml.cpp:1527
1527 for( TiXmlAttribute* node = sentinel.next; node != &sentinel; node = node->next )
There are a number of things, but they all come down to the same conceptual error.
XML nodes include text, whitespace, and attributes, not just elements.
So if you intend to recurse over all elements, this is wrong:
return recFunction(node->FirstChild());
Instead it should be this:
return recFunction(node->FirstChildElement());
Likewise where you ask for NextSibling
you probably want NextSiblingElement
.
For example:
int key2 = stoi(node->NextSibling()->ToElement()->Attribute("key"));
If NextSibling
returns a text node, then ToElement
will return null.
Looking at the SEGV information:
TiXmlAttributeSet::Find (this=0x60, name=0x100018ede "key") at tinyxml.cpp:1527
This shows the this
pointer value of 0x60, which is unlikely to be valid. The most likely way to get such a pointer is by an unchecked pointer adjustment in a type cast of a null pointer. I.e. you have at some point used a null pointer, and either your code or library code has cast it to a related type.
In this case since node->ToText()
succeeded, then node->ToElement()
is bound to return null
since a node is either a text node or an element node. Therefore the return value of ToElement
is null, and calling Attribute
on it is an error.