Cannot find element inside a element?

Started by
3 comments, last by BiiXteR 7 years, 4 months ago

I'm trying to loop trough every single element in my XML file to see if any of their content matches the string specified (in this case the string is Earth)

However, it for some reason can't find the element Earth, however if the element in the actual XML file isn't inside a element other than the root element it can find it.

So it can, for example, find Sun and Moon.

Here's the XML file :


<?xml version="1.0" encoding="utf-8"?>

<Root>

  <Sun>Sun.</Sun>
  <Moon>Moon.</Moon>

  <Planets>

    <Earth>Im the earth.</Earth>
    <Mars>Im chocolate.</Mars>
    <Uranus>Hurr-Durr</Uranus>
    <SomeOtherPlanet>???</SomeOtherPlanet>
    
  </Planets>

  <AShip>Just a UFO passing by...</AShip>

</Root>

And here's what my function that finds the element looks like :


std::string XMLFile::GetStringAttribute(std::string element)
{
	if (mErr == true)
	{
		std::cout << "XML : Something went wrong, could not get attribute." << std::endl;
		return "XML_UNKNOWN_ERROR";
	}

	tinyxml2::XMLNode* rootElement = mDoc.FirstChildElement("Root");
	if (rootElement == nullptr)
	{
		std::cout << "XML : No Root element could be found inside file : " << mFileName.c_str() << std::endl;
		return "FAILED_TO_LOAD_ROOT_ELEMENT";
	}

	for (tinyxml2::XMLElement* elem = rootElement->FirstChildElement(element.c_str()); elem != NULL; elem = rootElement->NextSiblingElement(element.c_str()))
	{
		return elem->GetText();
	}

	std::cout << "XML : Failed to find element : " << element.c_str() << ", inside file : " << mFileName.c_str() << std::endl;
	return "FAILED_TO_FIND_ELEMENT";

}

And here's how I call that function :


std::cout << file.GetStringAttribute("Earth") << std::endl;

I also have this function to load the file, (I know this part is working) ;


bool XMLFile::LoadFile(std::string filepath)
{
	mFileName = filepath;

	if (mDoc.LoadFile(filepath.c_str()) < 0)
	{
		std::cout << "XML : Failed to load XML file : " << filepath.c_str() << std::endl;
		mErr = true;
		return false;
	}

}

And that function is called like this :


XMLFile file;
file.LoadFile("Data/XML_Data/test.xml");

XMLFile is the class which holds the 2 functions LoadFile() and GetStringAttribute()

And mDoc is just a XMLDocument variable :


tinyxml2::XMLDocument mDoc;

I think this is all the information needed to recreate the problem, unsure what I'm doing wrong.

Advertisement

loop trough every single element in my XML file to see if any of their content matches the string specified (in this case the string is Earth)


Technically it's not the content you're looking for, but the tag name. The content is the text between the tags.

The problem is that FirstChildElement only looks at children - not grandchildren, or great-grandchildren, etc. I don't know if TinyXML2 offers a 'find element anywhere' function; if not, you'll have to do this manually.

One way to approach this is to write a recursive function. It would examine a node, and call itself on any children of that node. Unfortunately recursive functions can be hard to understand.

TinyXml2 also offers a Visitor pattern where you can create a visitor that examines each node in turn and can do whatever you like when it finds it. Unfortunately writing visitor functions can also be hard to understand.

It seems you are iterating along the children of 'Root', while the data you are looking for is sitting one level deeper. So you would first need to grab the Planets node and iterate over that. You could also get the Planets node directly by using getElementsByTagName() or similar (I'm not that familiar with TinyXML).

loop trough every single element in my XML file to see if any of their content matches the string specified (in this case the string is Earth)

Technically it's not the content you're looking for, but the tag name. The content is the text between the tags.

it for some reason can't find the element Robin

Nor can I, because there's no element 'Robin'. Anyway....

The problem is that FirstChildElement only looks at children - not grandchildren, or great-grandchildren, etc. I don't know if TinyXML2 offers a 'find element anywhere' function; if not, you'll have to do this manually.

One way to approach this is to write a recursive function. It would examine a node, and call itself on any children of that node. Unfortunately recursive functions can be hard to understand.

TinyXml2 also offers a Visitor pattern where you can create a visitor that examines each node in turn and can do whatever you like when it finds it. Unfortunately writing visitor functions can also be hard to understand.

Yeah, sorry. Edited. Have no idea why I wrote Robin instead of Earth lol :P

Alright, that makes sense, I think I prefer the first approach over the visitor one.

I think I have an idea on how to implement it, I'll come back here if I have any questions, and if I succeed on implementing it I'll mark your answer.

loop trough every single element in my XML file to see if any of their content matches the string specified (in this case the string is Earth)


Technically it's not the content you're looking for, but the tag name. The content is the text between the tags.

The problem is that FirstChildElement only looks at children - not grandchildren, or great-grandchildren, etc. I don't know if TinyXML2 offers a 'find element anywhere' function; if not, you'll have to do this manually.

One way to approach this is to write a recursive function. It would examine a node, and call itself on any children of that node. Unfortunately recursive functions can be hard to understand.

TinyXml2 also offers a Visitor pattern where you can create a visitor that examines each node in turn and can do whatever you like when it finds it. Unfortunately writing visitor functions can also be hard to understand.

My idea didn't work out very well, but I used another system.

Instead of the location of the element being located automatically, the programmer (me) will have to do it manually like this :

Let's say I have this in my XML file :


<Batman>

  <Friends>
    
    <Robin> Hello </Robin>

    <AnotherRobin>

      <Bat> Hi </Bat>

    </AnotherRobin>

  </Friends>

</Batman>

Then I would have to do something like this to print whatever is inside the tags of "Robin" :


NextElement(Batman);
NextElement(Friends);
NextElement(Robin);
std::cout << GrabString() << std::endl;

Not the best, but it's good enough for what I'm using it for. :)

This topic is closed to new replies.

Advertisement