I am trying to figure out a way to load the text from an XML document I have created using TinyXML2. Here is the entire document.
<?xml version="1.0" encoding="UTF-8"?>
<map version="1.0" orientation="orthogonal" width="15" height="13" tilewidth="32" tileheight="32">
<tileset firstgid="1" name="Background" tilewidth="32" tileheight="32">
<image source="background.png" width="64" height="32"/>
</tileset>
<tileset firstgid="3" name="Block" tilewidth="32" tileheight="32">
<image source="block.png" width="32" height="32"/>
</tileset>
<layer name="Background" width="15" height="13">
<data encoding="base64">
AgAAAAIAAAACAAAA...
</data>
</layer>
<layer name="Block" width="15" height="13">
<data encoding="base64">
AwAAAAMAAAADAAAAAwAAAAM...
</data>
</layer>
</map>
Basically, I want to copy the text from <data> into a string called background only if the layer name is "Background".
I have gotten the other variables like so:
// Get the basic information about the level
version = doc.FirstChildElement("map")->FloatAttribute("version");
orientation = doc.FirstChildElement("map")->Attribute("orientation");
mapWidth = doc.FirstChildElement("map")->IntAttribute("width");
mapHeight = doc.FirstChildElement("map")->IntAttribute("height");
That works great because I know the element name and the attribute name. Is there a way to say get the doc.FirstChildElement("map")->FirstChildElement("layer") and if it == "Background", get the text.
How would I accomplish this?
I know this thread is quite old, but just in case someone perusing the internet might stumble upon this question as I have, I wish to point out that Xanx's answer can be simplified slightly.
In tinyxml2.h it says that for the function const char* Attribute( const char* name, const char* value=0 ) const, if the value parameter is not null, then the function only returns if value and name match. According to the comments in the file this:
if ( ele->Attribute( "foo", "bar" ) ) callFooIsBar();
can be written like this:
if ( ele->Attribute( "foo" ) ) {
if ( strcmp( ele->Attribute( "foo" ), "bar" ) == 0 ) callFooIsBar();
}
So the code Xanx provided can be rewritten like this:
XMLElement * node = doc.FirstChildElement("map")->FirstChildElement("layer");
std::string value;
if (node->Attribute("name", "Background")) // no need for strcmp()
{
value = node->FirtChildElement("data")->GetText();
}
A minor change, yes, but something I wanted to add.
I advice you to do something like this:
XMLElement * node = doc.FirstChildElement("map")->FirstChildElement("layer");
std::string value;
// Get the Data element's text, if its a background:
if (strcmp(node->Attribute("name"), "Background") == 0)
{
value = node->FirtChildElement("data")->GetText();
}
auto bgData = text (find_element (doc, "map/layer[#name='Background']/data"));
Using tinyxml2 extension (#include <tixml2ex.h>).
N.B. should really be wrapped in a try/catch block.
Work in progress and documentation is incomplete (can deduce from the test example until it's ready).
I'll mention in passing that the other two answers only work properly when the desired <layer> element appears first.
Related
I have a xml file like this...
XML File
<?xml version="1.0" encoding="UTF-8"?>
<database xmlns="tt:foo" xmlns:h4a="https://www.myexample.com/foo.xsd" >
<tables>
<table name="my_first_table_name">
<column name="column_name_1" />
</table>
</tables>
</database>
I 'm trying to use DOMXPath for a future xml merging.
PHP Code
$doc_ref = new \DOMDocument();
$doc_ref->load( $array_xml_paths[0] );
$xpath_ref = new \DOMXPath($doc_ref);
error_log_array( $xpath_ref );
foreach ($xpath_ref->evaluate('//table[#name]') as $table) {
$table_name = $table->getAttribute('name');
error_log( $table_name );
}
output
[08-Mar-2018 14:21:47 UTC] DOMXPath Object
(
[document] => (object value omitted)
)
The DOMDocument is correclty loaded, but something more seems to be necessary for DOMXPath to work it but I don't know.
First of all...
[document] => (object value omitted) DOES NOT MEAN the DOMXPath instance is empty !
I found the solution. It was in this comment.
I had to register the xml namespace to use evaluate().
Solution
$doc_ref = new \DOMDocument();
$doc_ref->load( $array_xml_paths[0] );
$xpath_ref = new \DOMXPath( $doc_ref );
$ns = $doc_ref->documentElement->namespaceURI;
if($ns) {
$xpath_ref->registerNamespace("ns", $ns);
foreach ($xpath_ref->evaluate('//ns:table[#name]') as $table) {
$table_name = $table->getAttribute('name');
error_log( $table_name );
}
}else {
foreach ($xpath_ref->evaluate('//table[#name]') as $table) {
$table_name = $table->getAttribute('name');
error_log( $table_name );
}
}
My test.xml like this:
<?xml version="1.0"?>
<!DOCTYPE PLAY SYSTEM "play.dtd">
<data>
<CurrentLevel>5</CurrentLevel>
<BestScoreLV1>1</BestScoreLV1>
<BestScoreLV2>2</BestScoreLV2>
</data>
<dict/>
My Code here:
std::string fullPath = CCFileUtils::sharedFileUtils()->fullPathFromRelativePath("text.xml");
tinyxml2::XMLDocument doc;
doc.LoadFile(fullPath.c_str());
tinyxml2::XMLElement* ele = doc.FirstChildElement("data")->FirstChildElement("BestScoreLV2")->ToElement();
ele->SetAttribute("value", 10);
doc.SaveFile(fullPath.c_str());
const char* title1 = doc.FirstChildElement("data")->FirstChildElement("BestScoreLV2")->GetText();
int level1 = atoi(title1);
CCLOG("result is: %d",level1);
But value of BestScoreLV2 when output is also 2. How can I change and write data to XML?
In TinyXML2 text is represented by XMLText class which is child of XMLNode class.
XMLNode have methods Value() and SetValue() which have different meanings for different XML nodes.
For text nodes Value() read node's text and SetValue() write it.
So you need code like this:
tinyxml2::XMLNode* value = doc.FirstChildElement("data")->
FirstChildElement("BestScoreLV2")->FirstChild();
value->SetValue("10");
The first child of BestScoreLV2 element is XMLText with value 2. You change this value to 10 by calling SetValue(10).
I am trying to parse a gpx file using QXmlQuery (Qt 5.0).
The idea is to gather all the trkpt elements, and then subquery each element in order to extract latitude, longitude, altitude...
The problem is that it seems to not recognize the attribute name, because this code works:
query.setQuery
(
"declare default element namespace \"http://www.topografix.com/GPX/1/1\";"
"declare variable $gpxFile external;"
"doc($gpxFile)//trkpt"
);
if(query.isValid())
{
QXmlResultItems trkpts;
query.evaluateTo(&trkpts);
for(QXmlItem trkpt = trkpts.next(); !trkpt.isNull(); trkpt = trkpts.next())
{
QXmlQuery childQuery;
QStringList res;
childQuery.setFocus(trkpt);
childQuery.setQuery
(
"#*/string()"
); // <------------------------- this prints out all the attributes
childQuery.evaluateTo(&res);
qDebug() << res << "\n";
}
}
while this one does not:
// ... same code as above
childQuery.setQuery
(
"#lat/string()"
); // <------------------------- this prints out only a \n
// ... same code as above
any idea about what is wrong?
Please note that if I try to gather all the #lat attributes using the "outer" query, it works as expected:
query.setQuery
(
"declare default element namespace \"http://www.topografix.com/GPX/1/1\";"
"declare variable $gpxFile external;"
"doc($gpxFile)//trkpt/#lat/string()"
);
EDIT
This is a gpx sample file:
<?xml version="1.0" encoding="ISO-8859-1" standalone="yes"?>
<gpx
version="1.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.topografix.com/GPX/1/0"
xmlns:topografix="http://www.topografix.com/GPX/Private/TopoGrafix/0/2"
xsi:schemaLocation="http://www.topografix.com/GPX/1/0 http://www.topografix.com/GPX/1/0/gpx.xsd http://www.topografix.com/GPX/Private/TopoGrafix/0/2 http://www.topografix.com/GPX/Private/TopoGrafix/0/2/topografix.xsd">
<trk>
<name>My track</name>
<desc>My track description</desc>
<trkseg>
<trkpt lat="38.919839863" lon="-121.020112049">
<ele>265.447754</ele>
<time>2003-02-05T18:19:20Z</time>
</trkpt>
<trkpt lat="38.919796947" lon="-121.020240795">
<ele>264.967041</ele>
<time>2003-02-05T18:19:20Z</time>
</trkpt>
<trkpt lat="38.919861320" lon="-121.020498287">
<ele>263.044434</ele>
<time>2003-02-05T18:19:20Z</time>
</trkpt>
<trkpt lat="38.919990066" lon="-121.020798694">
<ele>263.525146</ele>
<time>2003-02-05T18:19:20Z</time>
</trkpt>
</trkseg>
</trk>
</gpx>
EDIT
I found a workaround for this problem, by using QDomDocument instead of QXmlQuery.
The result looks like the code below (all error checks removed for sake of brevity):
QFile file = ("mytrack.gpx");
file.open(QIODevice::ReadOnly);
QDomDocument doc("mydoc");
doc.setContent(&file);
QDomElement root = doc.documentElement();
QDomNodeList trkpts = root.elementsByTagName("trkpt");
for(int i = 0; i < trkpts.length(); i++)
{
QDomElement e = trkpts.at(i).toElement();
QString latitude = e.attribute("lat");
QString longitude = e.attribute("lon");
QDomNodeList elevation_list = e.elementsByTagName("ele");
QString elevation = elevation_list.at(0).toElement().text();
// ... and so on
}
I would like to find a solution to this problem by using QXmlQuery, but in the meantime this just works.
I am not sure, because I don't know QXmlQuery and how it handles query parsing and execution. But I guess it could be a namespace issue. In the first query you define a default namespace, while in the second one you do not. Maybe this default namespace is not used in the second query (childQuery). At least it would fit to #*/String() printing something. Maybe you could try if #*:lat/string works as expected.
Otherwise, some example data would be very valuable.
How can I get content between tags
<name> </name> and <mode> </mode>
<news>
<name>Enter</name>
<actions>
<mode>me</mode>
</actions>
</news>
You should really have a look at the excellent documentation of TinyXML as well as the tutorial. However, what you are looking for is the GetText() method of TiXmlElement.
Once you've arrived at your "name" or "mode" elements, you can get the string between these tags with GetText().
\ this
TiXmlDocument doc("tes.xml");
if (doc.LoadFile())
{
TiXmlHandle hDoc(&doc);
TiXmlText* text = hDoc.ChildElement("news", 0).ChildElement("act-news", 0).ChildElement("name", 0).FirstChild().Text();
if(text)
{
const char* message = text->Value();
cout<<(message)<<endl;
}
TiXmlText* stext = hDoc.ChildElement("news", 0).ChildElement("act-news", 1).ChildElement("name", 0).FirstChild().Text();
if(text)
{
const char* message = stext->Value();
cout<<(message)<<endl;
I am working in C++. I would like to ask how to obtain the value text from:
<message> text </message>
I have
TiXmlHandle handle(&doc);
TiXmlElement* section;
section=doc.FirstChildElement("message");
How to do it from now on? I know I have to work with .Element() but I don't know how.
You can use the function GetText() to obtain the contents of <message>. I put your XML-contents in a file called dummy.xml and used the following code to print the contents:
TiXmlDocument doc("dummy.xml");
if(doc.LoadFile())
{
TiXmlHandle hDoc(&doc);
TiXmlElement *pRoot;
pRoot = doc.FirstChildElement("message");
printf("pRoot text: %s", pRoot->GetText());
}