Reputation: 24916
How does one iterate nodes in TinyXML2? I tried following the documentation but am not able to grasp this.
http://www.grinninglizard.com/tinyxml2docs/index.html
My XML is already loaded into std::string
. Thus, the following compiles:
#include "tinyxml2.hpp"
// assume I have code here which reads my XML into std::string sXML
tinyxml2::XMLDocument doc;
doc.Parse( sXML.c_str() );
Now what do I do with doc
to iterate the item list so that I can pull out the title and author fields inside into std::string
variables?
Here's my XML sample:
<?xml version=“1.0” encoding=“utf-8”?>
<books>
<item>
<title>Letters to Gerhardt</title>
<author>Von Strudel, Jamath</author>
</item>
<item>
<title>Swiss Systemic Cleanliness Principles, The</title>
<author>Jöhansen, Jahnnes</author>
</item>
</books>
Was hoping for something simple like a C++ vector
of item
and then perhaps a C++ map
inside where I can address it by "title"
and "author"
or .title
or .author
.
Upvotes: 0
Views: 6181
Reputation: 1
Maybe this could be of help - quick implementation of missing GetElementsByTagName (...) that I used a lot in other XML DOM APIs:
bool ScanNodeForElementsByTagName (std::vector<XMLNode*>& vecNodeList, const std::string strTagName, XMLNode* pclNode)
{
bool bRetValue = true;
// check, if node is != null
if (pclNode == NULL)
{
bRetValue = false;
}
else
{
// check, if node itself has the target tag name
if (pclNode->ToElement()->Name() == strTagName)
{
vecNodeList.push_back (pclNode);
}
XMLNode* e = pclNode->FirstChild();
while (e != NULL)
{
if (e->ToElement() != NULL)
{
ScanNodeForElementsByTagName (vecNodeList, strTagName, e);
}
e = e->NextSibling();
}
}
return (bRetValue);
}
// MWI: added GetElementsByTagName function
std::vector<XMLNode*> XMLNode::GetElementsByTagName (const std::string strTagName, XMLNode* pclNode)
{
std::vector<XMLNode*> vecNodeList;
vecNodeList.clear ();
ScanNodeForElementsByTagName (vecNodeList, strTagName, pclNode);
return (vecNodeList);
}
Upvotes: 0
Reputation: 4467
Probably the best way to do this is to implement an XMLVisitor
class and use the XMLNode::Accept()
method. Then in your callback, you can grab the strings you want.
Upvotes: 1
Reputation: 1181
Here's some work in progress you might find useful: tinyxml2 extension . Documentation is incomplete so you'll need to deduce from the test example until it's finished.
You can read data from the xml with something like this:
#include <iostream>
#include <tixml2ex.h>
auto doc = tinyxml2::load_document (sXML);
for (auto item : selection (*doc, "books/item"))
{
std::cout << "title : " << text (find_element (item, "title")) << std::endl;
std::cout << "author : " << text (find_element (item, "author")) << std::endl << std::endl;
}
N.B. should really be wrapped in a try/catch block.
If you want to store element names and attributes as some combination of vector and map you'll have to copy over as required.
Upvotes: 1
Reputation: 24916
// PARSE BOOKS
#pragma once
#include <string>
#include <stdio.h>
#include <vector>
#include "tinyxml2.hpp"
struct myRec {
std::string title;
std::string author;
};
std::vector<myRec> recs;
tinyxml2::XMLDocument doc;
doc.Parse( sXML.c_str() );
tinyxml2::XMLElement* parent = doc.FirstChildElement("books");
tinyxml2::XMLElement *row = parent->FirstChildElement();
while (row != NULL) {
tinyxml2::XMLElement *col = row->FirstChildElement();
myRec rec;
while (col != NULL) {
std::string sKey;
std::string sVal;
char *sTemp1 = (char *)col->Value();
if (sTemp1 != NULL) {
sKey = static_cast<std::string>(sTemp1);
} else {
sKey = "";
}
char *sTemp2 = (char *)col->GetText();
if (sTemp2 != NULL) {
sVal = static_cast<std::string>(sTemp2);
} else {
sVal = "";
}
if (sKey == "title") {
rec.title = sVal;
}
if (sKey == "author") {
rec.author = sVal;
}
col = col->NextSiblingElement();
} // end while col
recs.push_back(rec);
row = row->NextSiblingElement();
} // end while row
signed long nLen = recs.size();
if (nLen > 0) {
--nLen;
nLen = (nLen < 0) ? 0 : nLen;
for (int i = 0; i <= nLen; i++) {
std::string sTitle = recs[i].title;
std::string sAuthor = recs[i].author;
std::cout << sTitle << "\n" << sAuthor << "\n";
}
} else {
std::cout << "Empty rowset of books.\n";
}
Note, I'm fairly new to C++. If you know of a way to optimize this in less lines, I'd be thrilled to see it.
Upvotes: 1