It's my first time using XML and I am currently trying to return an integer (actually want to return a double but haven't got that far yet) from an XML-file using C++. I'm using RAPIDXML and the following implementation:
All files are in the same directory.
XML (firstxml.xml):
<?xml version="1.0"?>
<test xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="firstxsd.xsd">
<A>10</A>
<B>Hello</B>
</test>
XML-Schema (firstxsd.xsd):
<xs:schema attributeFormDefault="unqualified" elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="test">
<xs:complexType>
<xs:sequence>
<xs:element type="xs:integer" name="A"/>
<xs:element type="xs:string" name="B"/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>
C++ (test.cxx):
#include <iostream>
#include <sstream>
#include <fstream>
#include "rapidxml-1.13/rapidxml.hpp"
#include "rapidxml-1.13/rapidxml_print.hpp"
#include <string>
#include <stdio.h>
#include <vector>
int main(int argc, char* argv[])
{
std::ifstream file ("firstxml.xml");
if (file.is_open())
{
file.seekg(0,std::ios::end);
int size = file.tellg();
file.seekg(0,std::ios::beg);
char* buffer = new char [size];
file.read (buffer, size);
file.close();
rapidxml::xml_document<> doc;
doc.parse<0>(buffer);
rapidxml::xml_node<> *node = doc.first_node()->first_node();
//Line which results in error
std::cout << node->value()*10 << std::endl;
delete[] buffer;
}
}
The error:
test.cxx:52:31: error: invalid operands of types ‘char*’ and ‘int’ to binary ‘operator*’
From the tutorials I have read online, I believe I am constructing the files correctly and so the value parsed into the C++ file from the node A should be an integer. One thing I did notice is that in the RAPIDXML manual the specification of value() is as follows:
Ch* value() const;
Description: Gets value of node. Interpretation of value depends on type of node. Note that value will not be zero-terminated if rapidxml::parse_no_string_terminators option was selected during parse. Use value_size() function to determine length of the value.
Returns: Value of node, or empty string if node has no value.
So the function definition says it always returns a character pointer, but the line "Interpretation of value depends on type of node" implies it returns a type-dependant value.
Thank you for taking the time to look at my issue, any help is greatly appreciated,
Paul.
It is hard to say what does the phrase "Interpretation of value depends on type of node" mean. If the function returns Ch pointer
it will always return such pointer or cause a runtime exception, if library developers provided such feature. Naturally, when you try to multiply a Ch*
by an int you get compile time error.
The only way a function can return a type which depends on call context is to use some special user-defined return type (VARIANT
or something like it). It seems to me that Ch
is just a pseudonim for the basic character type of the library (char
or maybe wchar_t
).
So by that "interpretation..." notice developers probably meant that the user must make a proper interpretation of the character string in accordance to the type of node (which in turn depends on the user-defined schema).
To convert a C-string representation of a number to corresponding types you may use functions from the standard C library, such as atoi()
(converts to int), atof()
(to double) and so on.
I guess you want to access "attribute" value, so do something like ...
cout << "Name of my first node is: " << doc.first_node()->name() << "\n";
xml_node<> *node = doc.first_node("foobar");
cout << "Node foobar has value " << node->value() << "\n";
for (xml_attribute<> *attr = node->first_attribute();
attr; attr = attr->next_attribute())
{
cout << "Node foobar has attribute " << attr->name() << " ";
cout << "with value " << attr->value() << "\n";
}
And also have a look at rapidXML tutorial...
http://rapidxml.sourceforge.net/manual.html its easy!
Someone correct me if I'm wrong, I believe RapidXML will only give you values as some form of string due to its in-place parsing nature. The class templating might be for handling char vs wide char or something like that, I don't know, I only use char.
What I did to handle extracting values was to add a helper class to rapidxml_utils.hpp (could really have put it anywhere but it made sense there) with some static functions to parse a xml node or attribute to a value (or a default value if it doesn't parse).
These helpers have allowed me to do stuff like:
int Height = xml_helper::GetNodeInt(cur_node->first_node("Height"), 48);
int Width = xml_helper::GetNodeInt(cur_node->first_attribute("Width"), 128);
In your case you might use my helper class like this:
//Line which results in error (altered to use my helper class, if node absent or not parse as integer uses zero)
std::cout << xml_helper::GetNodeInt(node, 0)*10 << std::endl;
My helper class looks like this (I used sscanf to get some level of validation):
class xml_helper
{
public:
// a couple little helpers, lets you do this:
// int Height = xml_helper::GetNodeInt(cur_node->first_node("Height"), 48);
// int Width = xml_helper::GetNodeInt(cur_node->first_node("Width"), 128);
static int GetNodeInt(rapidxml::xml_base<>* node, int DefaultValue)
{
int temp;
try
{
if (node == 0) return DefaultValue;
if (sscanf(node->value(), "%d", &temp) != 1) return DefaultValue;
return temp;
//return atoi(node->value());
}
catch (...) { return DefaultValue; }
}
static unsigned int GetNodeUInt(rapidxml::xml_base<>* node, unsigned int DefaultValue)
{
unsigned int temp;
try
{
if (node == 0) return DefaultValue;
if (sscanf(node->value(), "%u", &temp) != 1) return DefaultValue;
return temp;
//return strtoul(node->value(), NULL, 0);
}
catch (...) { return DefaultValue; }
}
static long long GetNodeLL(rapidxml::xml_base<>* node, long long DefaultValue)
{
long long temp;
try
{
if (node == 0) return DefaultValue;
if (sscanf(node->value(), "%lld", &temp) != 1) return DefaultValue; //C99 covers %lld
return temp;
//return strtoll(node->value(), NULL, 0); //C++11, could use _strtoi64 in VS until then?
}
catch (...) { return DefaultValue; }
}
static double GetNodeDouble(rapidxml::xml_base<>* node, double DefaultValue)
{
double temp;
try
{
if (node == 0) return DefaultValue;
if (sscanf(node->value(), "%lf", &temp) != 1) return DefaultValue; // could use strtod
return temp;
}
catch (...) { return DefaultValue; }
}
static std::string GetNodeStr(rapidxml::xml_base<>* node, std::string DefaultValue)
{
try
{
if (node == 0) return DefaultValue;
return node->value();
}
catch (...) { return DefaultValue; }
}
};
Hopefully this may be helpful to you or someone else using rapidxml and needing a quick way to get integers, doubles, or whatever out of nodes. It can certainly be expanded to other types, I only have types I've needed so far in there now, but you get the gist of it.