tinyxml parsing xml file - c++

I have a xml file like this:
<?xml version="1.0"?>
<ApplicationSettings>
<BeamGeometry
Dimension="2"
Type="fan"
Shape="arc"
LengthFocalPointToISOCenter="558"
LengthISOCenterToDetector="394"
LengthDetectorSeperation="0.98"
LengthModuleSeperation="0.04"
NumberModules="57"
NumberDetectorsPerModule="16"
NumberISOCenterShift="3.25" />
</ApplicationSettings>
And I'd like to use tinyxml retrieving all the values (such as 558) based on the entry name such as (LengthFocalPointToISOCenter). Here is my code, not successful yet.
int SetFanbeamGeometry(const char* filename)
{
int ret = TRUE;
TiXmlDocument doc("E:\\Projects\\iterativeRecon\\ProjectPackage\\ApplicationSettings\\ApplicationSettings.xml");
int LengthFocalPointToISOCenter;
if( doc.LoadFile())
{
TiXmlHandle hDoc(&doc);
TiXmlElement *pRoot, *pParm;
pRoot = doc.FirstChildElement("ApplicationSettings");
if(pRoot)
{
pParm = pRoot->FirstChildElement("BeamGeometry");
int i = 0; // for sorting the entries
while(pParm)
{
pParm = pParm->NextSiblingElement("BeamGeometry");
i++;
}
}
}
else
{
printf("Warning: ApplicationSettings is not loaded!");
ret = FALSE;
}
return ret;
}
I am wondering how can I use tinyxml to do that? Sorry I am a first time user. and it looks confusing to me. Thanks.

There's only one BeamGeometry child element in the snippet you've shown; the information you're trying to access are its attributes - they're not individual elements.
So you need something like this:
// ...
pParm = pRoot->FirstChildElement("BeamGeometry");
if (pParm)
{
const char* pAttr = pParm->Attribute("LengthFocalPointToISOCenter");
if (pAttr)
{
int iLengthFocalPointToISOCenter = strtoul(pAttr, NULL, 10);
// do something with the value
}
}
If you want to loop through all attributes, it's quite simple:
const TiXmlAttribute* pAttr = pParm->FirstAttribute();
while (pAttr)
{
const char* name = pAttr->Name(); // attribute name
const char* value = pAttr->Value(); // attribute value
// do something
pAttr = pAttr->Next();
}

Related

using a bytes field as proxy for arbitrary messages

Hello nano developers,
I'd like to realize the following proto:
message container {
enum MessageType {
TYPE_UNKNOWN = 0;
evt_resultStatus = 1;
}
required MessageType mt = 1;
optional bytes cmd_evt_transfer = 2;
}
message evt_resultStatus {
required int32 operationMode = 1;
}
...
The dots denote, there are more messages with (multiple) primitive containing datatypes to come. The enum will grow likewise, just wanted to keep it short.
The container gets generated as:
typedef struct _container {
container_MessageType mt;
pb_callback_t cmd_evt_transfer;
} container;
evt_resultStatus is:
typedef struct _evt_resultStatus {
int32_t operationMode;
} evt_resultStatus;
The field cmd_evt_transfer should act as a proxy of subsequent messages like evt_resultStatus holding primitive datatypes.
evt_resultStatus shall be encoded into bytes and be placed into the cmd_evt_transfer field.
Then the container shall get encoded and the encoding result will be used for subsequent transfers.
The background why to do so, is to shorten the proto definition and avoid the oneof thing. Unfortunately syntax version 3 is not fully supported, so we can not make use of any fields.
The first question is: will this approach be possible?
What I've got so far is the encoding including the callback which seems to behave fine. But on the other side, decoding somehow skips the callback. I've read issues here, that this happened also when using oneof and bytes fields.
Can someone please clarify on how to proceed with this?
Sample code so far I got:
bool encode_msg_test(pb_byte_t* buffer, int32_t sval, size_t* sz, char* err) {
evt_resultStatus rs = evt_resultStatus_init_zero;
rs.operationMode = sval;
pb_ostream_t stream = pb_ostream_from_buffer(buffer, sizeof(buffer));
/*encode container*/
container msg = container_init_zero;
msg.mt = container_MessageType_evt_resultStatus;
msg.cmd_evt_transfer.arg = &rs;
msg.cmd_evt_transfer.funcs.encode = encode_cb;
if(! pb_encode(&stream, container_fields, &msg)) {
const char* local_err = PB_GET_ERROR(&stream);
sprintf(err, "pb_encode error: %s", local_err);
return false;
}
*sz = stream.bytes_written;
return true;
}
bool encode_cb(pb_ostream_t *stream, const pb_field_t *field, void * const *arg) {
evt_resultStatus* rs = (evt_resultStatus*)(*arg);
//with the below in place a stream full error rises
// if (! pb_encode_tag_for_field(stream, field)) {
// return false;
// }
if(! pb_encode(stream, evt_resultStatus_fields, rs)) {
return false;
}
return true;
}
//buffer holds previously encoded data
bool decode_msg_test(pb_byte_t* buffer, int32_t* sval, size_t msg_len, char* err) {
container msg = container_init_zero;
evt_resultStatus res = evt_resultStatus_init_zero;
msg.cmd_evt_transfer.arg = &res;
msg.cmd_evt_transfer.funcs.decode = decode_cb;
pb_istream_t stream = pb_istream_from_buffer(buffer, msg_len);
if(! pb_decode(&stream, container_fields, &msg)) {
const char* local_err = PB_GET_ERROR(&stream);
sprintf(err, "pb_encode error: %s", local_err);
return false;
}
*sval = res.operationMode;
return true;
}
bool decode_cb(pb_istream_t *istream, const pb_field_t *field, void **arg) {
evt_resultStatus * rs = (evt_resultStatus*)(*arg);
if(! pb_decode(istream, evt_resultStatus_fields, rs)) {
return false;
}
return true;
}
I feel, I don't have a proper understanding of the encoding / decoding process.
Is it correct to assume:
the first call of pb_encode (in encode_msg_test) takes care of the mt field
the second call of pb_encode (in encode_cb) handles the cmd_evt_transfer field
If I do:
bool encode_cb(pb_ostream_t *stream, const pb_field_t *field, void * const *arg) {
evt_resultStatus* rs = (evt_resultStatus*)(*arg);
if (! pb_encode_tag_for_field(stream, field)) {
return false;
}
if(! pb_encode(stream, evt_resultStatus_fields, rs)) {
return false;
}
return true;
}
then I get a stream full error on the call of pb_encode.
Why is that?
Yes, the approach is reasonable. Nanopb callbacks do not care what the actual data read or written by the callback is.
As for why your decode callback is not working, you'll need to post the code you are using for decoding.
(As an aside, Any type does work in nanopb and is covered by this test case. But the type_url included in all Any messages makes them have a quite large overhead.)

Rapidjson returning reference to Document Value

I'm having some trouble with the following method and I need some help trying to figure out what I am doing wrong.
I want to return a reference to a Value in a document. I am passing the Document from outside the function so that when I read a json file into it I don't "lose it".
const rapidjson::Value& CTestManager::GetOperations(rapidjson::Document& document)
{
const Value Null(kObjectType);
if (m_Tests.empty())
return Null;
if (m_current > m_Tests.size() - 1)
return Null;
Test& the_test = m_Tests[m_current];
CMyFile fp(the_test.file.c_str()); // non-Windows use "r"
if (!fp.is_open())
return Null;
u32 operations_count = 0;
CFileBuffer json(fp);
FileReadStream is(fp.native_handle(), json, json.size());
if (document.ParseInsitu<kParseCommentsFlag>(json).HasParseError())
{
(...)
}
else
{
if (!document.IsObject())
{
(...)
}
else
{
auto tests = document.FindMember("td_tests");
if (tests != document.MemberEnd())
{
for (SizeType i = 0; i < tests->value.Size(); i++)
{
const Value& test = tests->value[i];
if (test["id"].GetInt() == the_test.id)
{
auto it = test.FindMember("operations");
if (it != test.MemberEnd())
{
//return it->value; is this legitimate?
return test["operations"];
}
return Null;
}
}
}
}
}
return Null;
}
Which I am calling like this:
Document document;
auto operations = TestManager().GetOperations(document);
When I inspect the value of test["operations"] inside the function I can see everything I would expect (debug code removed from the abode code).
When I inspect the returned value outside the function I can see that it's an array (which I expect). the member count int the array is correct as well, but when print it out, I only see garbage instead.
When I "print" the Value to a string inside the methods, I get what I expect (i.e. a well formated json), but when I do it outside all keys show up as "IIIIIIII" and values that aren't strings show up correctly.
rapidjson::StringBuffer strbuf2;
rapidjson::PrettyWriter<rapidjson::StringBuffer> writer2(strbuf2);
ops->Accept(writer2);
As this didn't work I decided to change the method to receive a Value as a parameter and do a deep copy into it like this
u32 CTestManager::GetOperationsEx(rapidjson::Document& document, rapidjson::Value& operations)
{
(...)
if (document.ParseInsitu<kParseCommentsFlag>(json).HasParseError())
{
(...)
}
else
{
if (!document.IsObject())
{
(...)
}
else
{
auto tests = document.FindMember("tests");
if (tests != document.MemberEnd())
{
for (SizeType i = 0; i < tests->value.Size(); i++)
{
const Value& test = tests->value[i];
if (test["id"].GetInt() == the_test.id)
{
const Value& opv = test["operations"];
Document::AllocatorType& allocator = document.GetAllocator();
operations.CopyFrom(opv, allocator); //would Swap work?
return operations.Size();
}
}
}
}
}
return 0;
}
Which I'm calling like this:
Document document;
Value operations(kObjectType);
u32 count = TestManager().GetOperationsEx(document, operations);
But... I get same thing!!!!
I know that it's going to be something silly but I can't put my hands on it!
Any ideas?
The problem in this case lies with the use of ParseInSitu. When any of the GetOperations exist the CFileBuffer loses scope and is cleaned up. Because the json is being parsed in-situ when the buffer to the file goes, so goes the data.

How check empty tag in xml with RapidXML c++

Exists XML:
<?xml version="1.0" encoding="UTF-8"?>
<ServerData>
<NetJobChunk id="599">
<Frames>0.000 - 0.000</Frames>
<EndTime>0</EndTime>
<ChunkID>1</ChunkID>
<StartDateTime>42212.713495</StartDateTime>
<RenderedFiles></RenderedFiles>
<AttemptsText>0</AttemptsText>
<RenderingTimeText>n/a</RenderingTimeText>
<Slice>Entire image</Slice>
<Attempts>0</Attempts>
<ErrorCount>0</ErrorCount>
<StartDateText>27.07.2015 17:07:26</StartDateText>
<RenderingClient></RenderingClient>
<Priority>0</Priority>
<StartTime>0</StartTime>
<NetJobID>599</NetJobID>
<ExtSplitting>None</ExtSplitting>
<AvgCPUUsage>0.000000</AvgCPUUsage>
<RenderingClientUuid>00000000-0000-0000-0000-000000000000</RenderingClientUuid>
<History></History>
<CacheScenesLocally>false</CacheScenesLocally>
<RenderingTime>0</RenderingTime>
<RecacheScenes>false</RecacheScenes>
<StateMachineState>0</StateMachineState>
<Scene>P:\\ftpuser\\F20141088\\Scene_news\\3dsmax\\test.max</Scene>
<StatusText>Done</StatusText>
<Status>4</Status>
</NetJobChunk>
<NetJobChunk id="599">
<Frames>1.000 - 1.000</Frames>
<EndTime>0</EndTime>
<ChunkID>2</ChunkID>
<StartDateTime>42212.713495</StartDateTime>
<RenderedFiles></RenderedFiles>
<AttemptsText>0</AttemptsText>
<RenderingTimeText>n/a</RenderingTimeText>
<Slice>Entire image</Slice>
<Attempts>0</Attempts>
<ErrorCount>0</ErrorCount>
<StartDateText>27.07.2015 17:07:26</StartDateText>
<RenderingClient></RenderingClient>
<Priority>0</Priority>
<StartTime>0</StartTime>
<NetJobID>599</NetJobID>
<ExtSplitting>None</ExtSplitting>
<AvgCPUUsage>0.000000</AvgCPUUsage>
<RenderingClientUuid>00000000-0000-0000-0000-000000000000</RenderingClientUuid>
<History></History>
<CacheScenesLocally>false</CacheScenesLocally>
<RenderingTime>0</RenderingTime>
<RecacheScenes>false</RecacheScenes>
<StateMachineState>0</StateMachineState>
<Scene>P:\\ftpuser\\F20141088\\Scene_news\\3dsmax\\test.max</Scene>
<StatusText>Done</StatusText>
<Status>4</Status>
</NetJobChunk>
<NetJobChunk id="601">
<Frames>0.000 - 0.000</Frames>
<EndTime>0</EndTime>
<ChunkID>1</ChunkID>
<StartDateTime>42212.852882</StartDateTime>
<RenderedFiles></RenderedFiles>
<AttemptsText>1 (max)</AttemptsText>
<RenderingTimeText>n/a</RenderingTimeText>
<Slice>Entire image</Slice>
<Attempts>1</Attempts>
<ErrorCount>1</ErrorCount>
<StartDateText></StartDateText>
<RenderingClient></RenderingClient>
<Priority>0</Priority>
<StartTime>0</StartTime>
<NetJobID>601</NetJobID>
<ExtSplitting>Camera: 005</ExtSplitting>
<AvgCPUUsage>0.000000</AvgCPUUsage>
<RenderingClientUuid>00000000-0000-0000-0000-000000000000</RenderingClientUuid>
<History>
<Value>1|1437999253|Rendering started [RENDERHOST202]|</Value>
<Value>4|1438010887|27.07.2015 20:28:07; Error rendering frame 0: An unexpected exception has occurred in the network renderer and it is terminating.|</Value>
<Value>4|1438010888|Chunk error: The renderer returned an error-code (0x3) [RENDERHOST202]|</Value>
<Value>1|1438010889|Rendering started [RENDERHOST202]|</Value>
<Value>3|1438017456|Chunk cancelled [RENDERHOST202]|</Value>
<Value>3|1438017456|Net Job cancelled by user|</Value>
</History>
<CacheScenesLocally>false</CacheScenesLocally>
<RenderingTime>0</RenderingTime>
<RecacheScenes>false</RecacheScenes>
<StateMachineState>0</StateMachineState>
<Scene>P:\\ftpuser\\F20131020\\2House_22_2015_07_27_12_16_55\\2House_22.max</Scene>
<StatusText>Cancelled</StatusText>
<Status>5</Status>
</NetJobChunk>
</ServerData>
It is required to receive the maximum value time (in ticks) of the last event of the tag "History":
<History>
<Value>...</Value>
</History>.
Tag "History" can be empty:
<History></History>.
Parsing:
...
char* ch = new char[xml.size()+1];
std::copy(xml.begin(), xml.end(), ch);
ch[xml.size()] = '\0';
xml_document<char> doc;
doc.parse<0>(ch);
unsigned long LastEventTime = 0;
xml_node<> *root_node = doc.first_node("ServerData");
for (xml_node<> * chunk_node = root_node->first_node("NetJobChunk"); chunk_node; chunk_node = chunk_node->next_sibling())
{
// History
unsigned long result = ParseChunkLastEventTime(chunk_node);
if(result > LastEventTime)
LastEventTime = result;
}
...
unsigned long CRapidXmlParser::ParseChunkLastEventTime(xml_node<char>* chunk_node) {
unsigned long val = 0;
if(chunk_node == nullptr)
throw std::exception("ParseChunkLastEventTime: null ptr chunk_node");
xml_node<> * chunk_hist_node = chunk_node->first_node("History");
xml_node<> * last_value_node = chunk_hist_node->last_node("Value");// ERROR!!!
if(last_value_node) {
std::string last_event = last_value_node->value();
size_t time_begin = last_event.find_first_of('|');
if(time_begin != std::string::npos) {
size_t time_end = last_event.find_first_of('|', time_begin+1);
if(time_end != std::string::npos) {
val = boost::lexical_cast<unsigned long>(last_event.substr(time_begin+1, time_end-time_begin-1) );
}
}
}
return val;
}
When I'm trying to get the last child tag ("Value") of an empty tag "History" - I get an exception, instead of the expected null pointer. What is the problem?
Need to use first_node(), to check if the node has children.
unsigned long CRapidXmlParser::ParseChunkLastEventTime(xml_node<char>* chunk_node) {
unsigned long val = 0;
if(chunk_node == nullptr)
throw std::exception("ParseChunkLastEventTime: null ptr chunk_node");
xml_node<> * chunk_hist_node = chunk_node->first_node("History");
if(chunk_hist_node->first_node("Value")) {
xml_node<> * last_value_node = chunk_hist_node->last_node("Value");
if(last_value_node) {
std::string last_event = last_value_node->value();
size_t time_begin = last_event.find_first_of('|');
if(time_begin != std::string::npos) {
size_t time_end = last_event.find_first_of('|', time_begin+1);
if(time_end != std::string::npos) {
val = boost::lexical_cast<unsigned long>(last_event.substr(time_begin+1, time_end-time_begin-1) );
}
}
}
}
return val;
}

Making an array for a static casted indexed array

I don't know if my title is right but I am trying to eliminate duplicate so I think I should put this definitions in an array. Can someone suggest me how I could put the pButtons in array? I am thinking something like pButton[EButtonHost], pButton[EButtonUsername] etc.
#define pButtonHost static_cast<XQtMultipleStringInputButton*>(m_pButtonList[EButtonHost])
#define pButtonUsername static_cast<XQtMultipleStringInputButton*>(m_pButtonList[EButtonUsername])
#define pButtonPassword static_cast<XQtMultipleStringInputButton*>(m_pButtonList[EButtonPassword])
I have a method below like this.
XIniFile readIniFile;
readIniFile.open(k_systemIniFile, EIniReadOnly);
string data;
readIniFile.readString("Server", "host", data);
pButtonHost->setString(data);
m_host = pButtonHost->getString();
readIniFile.readString("Server", "username", data);
pButtonUsername->setString(data);
m_username = pButtonUsername->getString();
readIniFile.readString("Server", "password", data);
pButtonPassword->setString(data);
m_password = pButtonPassword->getString();
They look like duplicates so I am trying to optimize it. Thanks!
Update:
I have something like this now. Would this be right? or do you have any better suggestions?
for (int i = 0; i < 3; ++i) {
readIniFile.readString("Server", k_dataList[i], data);
static_cast<XQtMultipleStringInputButton*>(m_pButtonList[i])->setString(data);
m_pData[i] = static_cast<XQtMultipleStringInputButton*>(m_pButtonList[i])->getString();
}
A below code looks clear by using auto and `lambda',
auto GetConfigInfo = [&](string section_name, ButtonType btn_type)-> string
{
readIniFile.readString("Server", section_name, data);
m_pButtonList[btn_type_]->setString(data);
return m_pButtonList->getString();
};
m_host = GetConfigInfo("host", EButtonHost);
m_username = GetConfigInfo("username", EButtonUserName);
m_password = GetConfigInfo("password", EButtonPassword);
and a define data type by using struct can be other method.
struct ConfigDefine
{
string section_;
ButtonType btn_type_; //your enum button type, such as EButtonHost, EButtonUserName
string& result_;
}configs[] =
{
{"host", EButtonHost, m_host},
{"username", EButtonUserName, m_username},
{"password", EButtonPassword, m_password}
};
for_each(std::begin(configs), std::end(configs), [&](ConfigDefine& config)
{
readIniFile.readString("Server", config.section_, data);
m_pButtonList[config.btn_type_]->setString(data);
config.result_ = pButtonHost->getString();
});
Do you intend to have something like this one?
#define ASIZE 3
int indx[ASIZE] = {EButtonHost, EButtonUsername, EButtonPassword};
string s[ASIZE] = {"host", "username", "password"};
string *m[ASIZE] = {&m_host, &m_username, &m_password};
for (int i = 0; i < ASIZE; i++) {
readIniFile.readString("Server", s[i].c_str(), data);
pButtonHost->setString(data);
*m[i] = pButtonHost->getString();
}

How to make upnp action?

I want to implement port-forwarding using intel-upnp.
I got XML data like:
Device found at location: http://192.168.10.1:49152/gatedesc.xml
service urn:schemas-upnp-org:service:WANIPConnection:1
controlurl /upnp/control/WANIPConn1
eventsuburl : /upnp/control/WANIPConn1
scpdurl : /gateconnSCPD.xml
And now, I want to make upnp-action. But, I don't know how to make it.
If you know some code snippet or helpful URL in C, please tell me.
char actionxml[250];
IXML_Document *action = NULL;
strcpy(actionxml, "<u:GetConnectionTypeInfo xmlns:u=\"urn:schemas-upnp- org:service:WANCommonInterfaceConfig:1\">");
action = ixmlParseBuffer(actionxml);
int ret = UpnpSendActionAsync( g_handle,
"http:192.168.10.1:49152/upnp/control/WANCommonIFC1",
"urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1",
NULL,
action,
upnp_callback,
NULL);
I know this is an old question, but it can be kept for reference. You can take a look at the sample code in the libupnp library here: https://github.com/mrjimenez/pupnp/blob/master/upnp/sample/common/tv_ctrlpt.c
The relevant code is in the function TvCtrlPointSendAction():
int TvCtrlPointSendAction(
int service,
int devnum,
const char *actionname,
const char **param_name,
char **param_val,
int param_count)
{
struct TvDeviceNode *devnode;
IXML_Document *actionNode = NULL;
int rc = TV_SUCCESS;
int param;
ithread_mutex_lock(&DeviceListMutex);
rc = TvCtrlPointGetDevice(devnum, &devnode);
if (TV_SUCCESS == rc) {
if (0 == param_count) {
actionNode =
UpnpMakeAction(actionname, TvServiceType[service],
0, NULL);
} else {
for (param = 0; param < param_count; param++) {
if (UpnpAddToAction
(&actionNode, actionname,
TvServiceType[service], param_name[param],
param_val[param]) != UPNP_E_SUCCESS) {
SampleUtil_Print
("ERROR: TvCtrlPointSendAction: Trying to add action param\n");
/*return -1; // TBD - BAD! leaves mutex locked */
}
}
}
rc = UpnpSendActionAsync(ctrlpt_handle,
devnode->device.
TvService[service].ControlURL,
TvServiceType[service], NULL,
actionNode,
TvCtrlPointCallbackEventHandler, NULL);
if (rc != UPNP_E_SUCCESS) {
SampleUtil_Print("Error in UpnpSendActionAsync -- %d\n",
rc);
rc = TV_ERROR;
}
}
ithread_mutex_unlock(&DeviceListMutex);
if (actionNode)
ixmlDocument_free(actionNode);
return rc;
}
The explanation is that you should create the action with UpnpMakeAction() if you have no parameters or UpnpAddToAction() if you have parameters to create your action, and then send it either synchronously or asynchronously.