tinyxml2 and how to read files

Started by
7 comments, last by MARS_999 10 years ago

I am trying to use tinyxml2 to read in a file like this...


<resources>
<resource UID="1" type="graphic" filename="smoke.png" scenescope="0"></resource>
<resource UID="2" type="graphic" filename="face_colorkey.png" scenescope="0"></resource>
<resource UID="3" type="graphic" filename="Tiles.png" scenescope="0"></resource>
<resource UID="4" type="audio" filename="myaudio.mp3" scenescope="0" audio_type="stream"></resource>
<resource UID="5" type="audio" filename="music.ogg" scenescope="0" audio_type="sample"></resource>
</resources>


tinyxml2::XMLDocument doc;
    if(doc.LoadFile(Filename.c_str()) == tinyxml2::XML_NO_ERROR)
    {
   int v0 = 0; 
tinyxml2::XMLElement* ResourceElement = doc.FirstChildElement("resources")->FirstChildElement("resource");
ResourceElement->QueryIntAttribute("UID", &v0);
std::cout << v0 << std::endl;
ResourceElement->QueryIntAttribute("scenescope", & v0);
std::cout << v0 << std::endl;
}

I don't see any ResourceElement->QueryStringAttribute() abilities so I am confused...

Thanks!

Advertisement

Ive only done a small test of tinyxml2 havent moved to it yet, but that looks like it should work the interface didnt change that much. Does it load correctly? If you set a break in the if statement is it reached? What about "doc.RootElement()->FirstChildElement("resource");" ?

There is const char* Attribute(const char* attribName) method in XMLElement you could use.

thanks guys I found this to work so far...

 
for(tinyxml2::XMLElement* child = doc.FirstChildElement("resources")->FirstChildElement("resource"); 
child != 0; child = child->NextSiblingElement())
{
if(child->ToElement()->Attribute("UID"))
std::cout << "UID = "        << child->ToElement()->Attribute("UID")        << std::endl;
if(child->ToElement()->Attribute("type"))
std::cout << "type = "      << child->ToElement()->Attribute("type")       << std::endl;
if(child->ToElement()->Attribute("filename"))
std::cout << "filename = "   << child->ToElement()->Attribute("filename")   << std::endl;
if(child->ToElement()->Attribute("scenescope"))
std::cout << "scenescope = " << child->ToElement()->Attribute("scenescope") << std::endl;
if(child->ToElement()->Attribute("audio_type"))
std::cout << "audio_type = " << child->ToElement()->Attribute("audio_type") << std::endl;
std::cout << std::endl;
} 

There is no need for you to call ToElement on child, because the for loop makes sure its an element already. And with FirstAttribute on an element you can actually iterate over the attributes of an element cleaning this code up a bit. Also it is better to use FindAttribute on element than just Attribute, you are not trying to match an Attribute with name and value, so FindAttribute actually expresses your intent better and makes your code more maintainable.

Worked on titles: CMR:DiRT2, DiRT 3, DiRT: Showdown, GRID 2, theHunter, theHunter: Primal, Mad Max, Watch Dogs: Legion

Cleaning up the 'const', removing the extra casts (as NightCreature83 suggests) and using a QueryAttribute to get an unsigned, the main loop looks like:


tinyxml2::XMLDocument doc;
if(doc.LoadFile("resources.xml") == tinyxml2::XML_NO_ERROR)
{
	for( const tinyxml2::XMLElement* child = doc.FirstChildElement("resources")->FirstChildElement("resource"); 
		 child; 
		 child = child->NextSiblingElement())
	{
		if(child->Attribute("UID"))
			std::cout << "UID = "        << child->Attribute("UID")        << std::endl;

		// assuming you want UID as an unsigned:
		unsigned uid = 0;
		child->QueryAttribute( "UID", &uid);
		std::cout << "UID = "        << uid        << std::endl;

		std::cout << std::endl;
	}
}
One more suggestion: save the results to non-constant-time functions like Attribute() in local variables. There's no good reason to ask the data structure to do the search twice. This isn't just a TinyXML-specific hint but in general a very good practice to get into. Don't look up twice what you can look up once.

unsigned uid = 0;
const char* uidString = child->Attribute("UID");
if (uidString != nullptr)
{
  uid = parseInteger(uidString);
  std::cout << "UID = " << uid << " (\"" << uidString << "\")\n";
}
While not critical in real game code, note also that I am _not_ using std::endl. That manipulator not only ends a line but also requests that output be flushed from internal buffers to the OS, which is not an efficient thing to do. It should only be used for logging output or other output that you need output even in case of a crash or if you need the output to appear immediately. For general output, use "\n" instead, and call std::cout << std::flush after performing a chunk of operations (for example, after you process the document) so that the output shows up even if the program goes off to do other things.

I know a lot of learning resources tell you to use std::endl at the end of a line but they are simply wrong. http://en.cppreference.com/w/cpp/io/manip/endl Note also how the example code at that link calls std::cout.sync_with_stdio(false) at the start of main, since even using only \n can cause unnecessary flushes for some runtime library implementations.

Sean Middleditch – Game Systems Engineer – Join my team!

There is no need to use string parsing on your own, the XMLAttribute in tinyXml has function to get the value of an attribute as an Int, Float and bool. So store a XMLAttribute* instead when you query for the attribute and you now have both tag name and attribute value stored in one handy reference.

Worked on titles: CMR:DiRT2, DiRT 3, DiRT: Showdown, GRID 2, theHunter, theHunter: Primal, Mad Max, Watch Dogs: Legion

Okay thanks all I will take a look later today at these posts!!!

This topic is closed to new replies.

Advertisement