Mapping C structure to an XML element - c++

Suppose I have a structure in C or C++, such as:
struct ConfigurableElement {
int ID;
char* strName;
long prop1;
long prop2;
...
};
I would like to load/save it to/from the following XML element:
<ConfigurableElement ID="1" strName="namedElem" prop1="2" prop2="3" ... />
Such a mapping can be trivially done in Java/C# or any other language with run-time reflection for the matter. Can it be done in any non-tedious way in C++ with macros/template trickery?
Bonus points for handling nested structures/unions.

The technique you want is called serialization. You may want to read these articles:
http://www.codeproject.com/KB/cpp/xmlserialization.aspx
http://www.codesynthesis.com/products/xsd/ <=== Very close to what you want!
http://www.artima.com/cppsource/xml_data_binding.html
http://www.ibm.com/developerworks/xml/library/x-serial.html
http://www.firstobject.com/xml-serialization-in-c++.htm
EDIT:
There is another option for you: Xmlize provided by Ultimatepp:
http://www.ultimatepp.org/reference$Xmlize.html
http://www.ultimatepp.org/reference$Xmlize$en-us.html
http://www.ultimatepp.org/reference$XmlizeCustomValue$en-us.html
http://www.ultimatepp.org/reference$Xmlize_std$en-us.html
http://www.ultimatepp.org/reference$XML$en-us.html

Tips and tricks always exists. Take a look at Metaresc library https://github.com/alexanderchuranov/Metaresc
It provides interface for types declaration that will also generate meta-data for the type. Based on meta-data you can easily serialize/deserialize objects of any complexity. Out of the box you can serialize/deserialize XML, JSON, XDR, Lisp-like notation, C-init notation.
Here is a simple example:
#include <stdio.h>
#include <stdlib.h>
#include "metaresc.h"
TYPEDEF_STRUCT (host_t,
(char *, host),
int port,
);
TYPEDEF_STRUCT (config_t,
(host_t, local),
(host_t, remote),
(char *, name),
);
int main (int argc, char * argv[])
{
config_t config = {
.local = {
.host = "localhost",
.port = 8080,
},
.remote = {
.host = "google.com",
.port = 80,
},
.name = "service",
};
char * str = MR_SAVE_XML (config_t, &config);
if (str)
{
printf ("%s\n", str);
free (str);
}
return (EXIT_SUCCESS);
}
This program will output
$ ./config
<?xml version="1.0"?>
<config>
<local>
<host>localhost</host>
<port>8080</port>
</local>
<remote>
<host>google.com</host>
<port>80</port>
</remote>
<name>service</name>
</config>
Library works fine for latest gcc and clang.

Related

Using C++ protobuf formatted structure in leveldb. set/get operations

I'd like to make a POC of using leveldb in order to store key-value table of different data types in protobuf format.
So far I was able to open the database file, and I also saw the get function with the following signature :
virtual Status Get(const ReadOptions& options, const Slice& key, std::string* value)=0
I understand that the value is actually refers to a binary string like vector and not regular alphanumeric string, so I guess it can fit for multi type primitives like string, uint, enum) but how can it support struct/class that represent protobuf layout in c++ ?
So this is my proto file that I'd like to store in the leveldb:
message agentStatus {
string ip = 1;
uint32 port = 2;
string url = 3;
google.protobuf.Timestamp last_seen = 4;
google.protobuf.Timestamp last_keepalive = 5;
bool status = 6;
}
and this is my current POC code. How can I use the get method to access any of the variables from the table above ?
#include <leveldb/db.h>
void main () {
std::string db_file_path = "/tmp/data.db";
leveldb::DB* db;
leveldb::Status status;
leveldb::Options options;
options.create_if_missing = false;
status_ = leveldb::DB::Open(options, db_file_path, &db);
if (!status_.ok()) {
throw std::logic_error("unable to open db");
}
Thanks !
You need to serialize the protobuf message into a binary string, i.e. SerilaizeToString, and use the Put method to write the binary string to LevelDB with a key.
Then you can use the Get method to retrieve the binary value with the given key, and parse the binary string to a protobuf message, i.e. ParseFromString.
Finally, you can get fields of the message.

BaseX XML database in C++ encoding issue

I work with Base X and try to integrate a XML database with c++ on Windows 7. I use the BaseXclient API from https://github.com/JohnLeM/BasexCPPAPI/
it contains the pugixml parser and uses the boost lib. I got it to work but I have issues with the encoding. The xml dokuments in my database are utf-8 and contain some letters and symbols that are not displayed correctly on console output(like ä and °).
I set the console code page with chcp 65001.
I changed the locale with std::setlocale(LC_ALL, ""); in c++ and when I cout these letters and symbols directly in my Programm and not from the database they are displayed correctly. The database output also changed but is still wrong.
I also set the pugi parser with pugi::xml_encoding::encoding_utf8; but the database output is not affected. here is a code example from the string list interface:
virtual ~my_string_list_interface() {};
my_string_list_interface(const std::string& DBHOST, const std::string& DBPORT, const std::string& DBUSER,const std::string& DBPASSWD) : base_type(DBHOST,DBPORT,DBUSER,DBPASSWD) {};
virtual my_string_list get(string query,int stage){
my_string_list my_string_list_;
pugi::xml_encoding::encoding_auto;
pugi::xml_parse_result parse_;
pugi::xml_document doc;
string results = session().execute("XQUERY "+query);
parse_ = doc.load(results.c_str());
pugi::xpath_node_set is = doc.select_nodes("/record/mid");
The API uses boost streambuf to get the data. The code from boost streambuf looks like that:
std::string read_streambuffer(){return read_streambuffer(response_);};
std::string read_streambuffer(boost::asio::streambuf & response)
{
std::string results;
boost::system::error_code error;
boost::asio::streambuf::const_buffers_type bufs = response.data();
std::size_t size(0);
std::string line;
auto ptr_b = boost::asio::buffers_begin(bufs);
for(; ptr_b != boost::asio::buffers_end(bufs); ++ptr_b, ++ size)
{if (*ptr_b != 0) {line.push_back(*ptr_b);} else if (size > 1) break; };
response.consume(size);
return line;
};
Is there a way to specify the encoding for the buffer stream or string? I use a string list for the database output. should I use wstring or is there something i missed?
Thanks!

Apache Thrift: When use "optional' before a list, C++ server seems not to return it correctly

Hi! every one, I have a problem similar with this one:
regarding thrift function return list
When a list has a "optional" modifier, the thrift C++ server will always return it as null/undef.
Additionally, if a struct contained by a optional list, any of the struct's field, can't be set to "optional", or, a null/undef value will be returned.
After deleting all the "optional" modifier, everything is OK.
Could anybody tell me why I can't use "optional" before a list ?
Here is the thrift file:
namespace cpp thrifttest
namespace perl thrifttest
struct Pair {
1: optional string a,
2: optional string b
}
struct Result {
1: optional list<string> strList,
2: optional list<Pair> pairList
}
service Test {
Result listTest()
}
Here is the C++ code (server):
#include "gen-cpp/Test.h"
#include <thrift/protocol/TBinaryProtocol.h>
#include <thrift/server/TSimpleServer.h>
#include <thrift/transport/TServerSocket.h>
#include <thrift/transport/TBufferTransports.h>
#include <iostream>
using namespace std;
using namespace ::apache::thrift;
using namespace ::apache::thrift::protocol;
using namespace ::apache::thrift::transport;
using namespace ::apache::thrift::server;
using boost::shared_ptr;
using namespace ::thrifttest;
class TestHandler : virtual public TestIf {
public:
TestHandler() {
// Your initialization goes here
}
void listTest(Result& _return) {
_return.strList.push_back("Test");
_return.strList.push_back("one level list");
cout << "strList size: " << _return.strList.size() << endl;
Pair pair;
pair.a = "Test";
pair.b = "two level list";
_return.pairList.push_back(pair);
cout << "pairList size: " << _return.pairList.size() << endl;
printf("call listTest\n");
}
};
int main(int argc, char **argv) {
int port = 9595;
shared_ptr<TestHandler> handler(new TestHandler());
shared_ptr<TProcessor> processor(new TestProcessor(handler));
shared_ptr<TServerTransport> serverTransport(new TServerSocket(port));
shared_ptr<TTransportFactory> transportFactory(new TBufferedTransportFactory());
shared_ptr<TProtocolFactory> protocolFactory(new TBinaryProtocolFactory());
TSimpleServer server(processor, serverTransport, transportFactory, protocolFactory);
server.serve();
return 0;
}
Here is the perl code (client):
#!/usr/bin/perl
use v5.12;
use warnings;
use autodie;
use utf8;
use Data::Dumper;
use lib 'gen-perl';
use thrifttest::Test;
use thrifttest::Constants;
use thrifttest::Types;
use Thrift;
use Thrift::BinaryProtocol;
use Thrift::Socket;
use Thrift::BufferedTransport;
my $socket = new Thrift::Socket('localhost', 9595);
my $transport = new Thrift::BufferedTransport($socket, 1024, 1024);
my $protocol = new Thrift::BinaryProtocol($transport);
my $client = new thrifttest::TestClient($protocol);
eval {
$transport->open();
my $result = $client->listTest;
say Dumper($result);
$transport->close();
};
say $# if $#;
C++ server output:
strList size: 2
pairList size: 1
call listTest
perl client output:
$VAR1 = bless( {
'pairList' => undef,
'strList' => undef
}, 'thrifttest::Result' );
PS: My development environment is CentOS 7, GCC 4.8.3, Perl 5.16, thrift 0.9.3
I was wrong, it is not a real bug, it is just some unfortunate design that is not really fool proof and allows the user to make Dumb Things™. The problem lies in the semantics of the optional operator and how it is implemented for C++.
Lets asssume we have this piece of IDL:
struct Xtruct2
{
1: i8 byte_thing,
2: Xtruct struct_thing,
3: i32 i32_thing
}
The generated code, in particular the write() method, looks like this:
uint32_t Xtruct2::write(::apache::thrift::protocol::TProtocol* oprot) const {
//...
xfer += oprot->writeFieldBegin("struct_thing", ::apache::thrift::protocol::T_STRUCT, 2);
xfer += this->struct_thing.write(oprot);
xfer += oprot->writeFieldEnd();
//...
}
If we now modify the IDL and add the optional specifier:
struct Xtruct2
{
1: i8 byte_thing,
2: optional Xtruct struct_thing,
3: i32 i32_thing
}
The generated code looks slightly different:
uint32_t Xtruct2::write(::apache::thrift::protocol::TProtocol* oprot) const {
//...
if (this->__isset.struct_thing) {
xfer += oprot->writeFieldBegin("struct_thing", ::apache::thrift::protocol::T_STRUCT, 2);
xfer += this->struct_thing.write(oprot);
xfer += oprot->writeFieldEnd();
}
//...
}
Thrift has three kinds of requiredness: required, optional and default. The latter is assumed implicitly if neither required nor optional was specified (that's why there is no special keyword for default requiredness). The semantics vis-à-vis reading and writing those fields is as follows:
requiredness write field? read field?
----------------------------------------------------------------------
required always always, must be present
(default) always if present, may be missing
optional only if set if present, may be missing
So what optional changes compared to the default is the behaviour of the write method. While a default field is always written, an optional field is only written conditionally. That is checked by means of the __isset flags, which consist of a bitset, one bit for each field that is not required. If the corresponding bit flag within __isset is set, the field value can be used. If the bit flag is not present, the field value has not been initialized and thus should not be used.
So far this would not be a much of a problem. But there's a trap that you managed to hit: Default and optional fields can be accessed and used in C++ even if the bit flag is not set because they are just there. In case of default requiredness, this is not a big deal: You assign your values, and since the field is always written, basically nothing bad happens. The bit flag for the field is set when the field is deserialized (see generated code).
However, things change when you opt-in to optional: Now all of a sudden you are responsible to set the flag properly, either by accessing __isset directly or by means of the generated setter method, in our case this one:
void Xtruct2::__set_struct_thing(const Xtruct& val) {
this->struct_thing = val;
__isset.struct_thing = true;
}
My assumption was that it should not be possible to access an optional field that has not been set, but it turned out that this seems to be by design. I still think that the design is a bit too much error prone here.

Parsing XML Elements using TinyXML

UPDATE: Still not working :( I have updated the code portion to reflect what I currently have.
This should be a pretty easy question for people who have used TinyXML. I'm attempting to use TinyXML to parse through an XML document and pull out some values. I figured out how to add in the library yesterday, and I have successfully loaded the document (hey, it's a start).
I've been reading through the manual and I can't quite figure out how to pull out individual attributes. After Googling around, I haven't found an example of my specific example, so perhaps someone here who has used TinyXML can help out. Below is a slice of the XML, and where I have started to parse it.
XML:
<EGCs xmlns="http://tempuri.org/XMLSchema.xsd">
<card type="EGC1">
<offsets>
[ ... ]
</offsets>
</card>
<card type="EGC2">
<offsets>
[ ... ]
</offsets>
</card>
</EGCs>
Loading/parsing code:
TiXmlDocument doc("EGC_Cards.xml");
if(doc.LoadFile())
{
TiXmlHandle hDoc(&doc);
TiXmlElement* pElem;
TiXmlHandle hRoot(0);
pElem = hDoc.FirstChildElement().Element();
if (!pElem) return false;
hRoot = TiXmlHandle(pElem);
//const char *attribval = hRoot.FirstChild("card").ToElement()->Attribute("card");
pElem = hDoc.FirstChild("EGCs").Child("card", 1).ToElement();
if(pElem)
{
const char* tmp = pElem->GetText();
CComboBox *combo = (CComboBox*)GetDlgItem(IDC_EGC_CARD_TYPE);
combo->AddString(tmp);
}
}
I want to pull out each card "type" and save it to a string to put into a combobox. How do I access this attribute member?
After a lot of playing around with the code, here is the solution! (With help from HERE)
TiXmlDocument doc("EGC_Cards.xml");
combo = (CComboBox*)GetDlgItem(IDC_EGC_CARD_TYPE);
if(doc.LoadFile())
{
TiXmlHandle hDoc(&doc);
TiXmlElement *pRoot, *pParm;
pRoot = doc.FirstChildElement("EGCs");
if(pRoot)
{
pParm = pRoot->FirstChildElement("card");
int i = 0; // for sorting the entries
while(pParm)
{
combo->InsertString(i, pParm->Attribute("type"));
pParm = pParm->NextSiblingElement("card");
i++;
}
}
}
else
{
AfxMessageBox("Could not load XML File.");
return false;
}
there should be a Attribute method that takes and attribut name as parameter see: http://www.grinninglizard.com/tinyxmldocs/classTiXmlElement.html
from the documentation I see the code would look like:
hRoot.FirstChildElement("card").ToElement()->Attibute("type");
However for the type of thing you are doing I would use XPATH if at all possible. I have never used it but the TinyXPath project may be helpful if you choose to go that route the link is: http://tinyxpath.sourceforge.net/
Hope this helps.
The documentation I am using to help you from is found at: http://www.grinninglizard.com/tinyxmldocs/hierarchy.html
What you need is to get the attribute type from the element card. So in your code it should be something like:
const char * attribval = hRoot.FirstChild("card").ToElement()->Attribute("card");

How to query HTML with x XPath expression in C++?

I have a webbrowser and I use DocumentComplete to read the current document from the WebBrowser (as IHTMLDocument2).
What's the easiest way to run xpath queries in that html doc? I am looking for something easy to use and lightweight.
I am using Visual Studio C++ 2010.
What's the easiest way to run xpath
queries in that html doc? I am looking
for something easy to use and
lightweight.
I am using Visual Studio C++ 2010.
Generally, XPath expressions cannot be evaluated against HTML documents.
However, if the HTML document is also an XHTML document (which is by definition a well-formed XML document), then XPath expressions can be evaluated against it.
In particular using MS Visual C++, one can use code like this:
#include <stdio.h>
#import <msxml3.dll>
using namespace MSXML2;
void dump_com_error(_com_error &e);
int main(int argc, char* argv[])
{
CoInitialize(NULL);
try{
IXMLDOMDocument2Ptr pXMLDoc = NULL;
HRESULT hr = pXMLDoc.CreateInstance(__uuidof(DOMDocument30));
// Set parser property settings
pXMLDoc->async = VARIANT_FALSE;
// Load the sample XML file
hr = pXMLDoc->load("hello.xsl");
// If document does not load report the parse error
if(hr!=VARIANT_TRUE)
{
IXMLDOMParseErrorPtr pError;
pError = pXMLDoc->parseError;
_bstr_t parseError =_bstr_t("At line ")+ _bstr_t(pError->Getline())
+ _bstr_t("\n")+ _bstr_t(pError->Getreason());
MessageBox(NULL,parseError, "Parse Error",MB_OK);
return 0;
}
// Otherwise, build node list using SelectNodes
// and returns its length as console output
else
pXMLDoc->setProperty("SelectionLanguage", "XPath");
// Set the selection namespace URI if the nodes
// you wish to select later use a namespace prefix
pXMLDoc->setProperty("SelectionNamespaces",
"xmlns:xsl='http://www.w3.org/1999/XSL/Transform'");
IXMLDOMElementPtr pXMLDocElement = NULL;
pXMLDocElement = pXMLDoc->documentElement;
IXMLDOMNodeListPtr pXMLDomNodeList = NULL;
pXMLDomNodeList = pXMLDocElement->selectNodes("//xsl:template");
int count = 0;
count = pXMLDomNodeList->length;
printf("The number of <xsl:template> nodes is %i.\n", count);
}
catch(_com_error &e)
{
dump_com_error(e);
}
return 0;
}
void dump_com_error(_com_error &e)
{
printf("Error\n");
printf("\a\tCode = %08lx\n", e.Error());
printf("\a\tCode meaning = %s", e.ErrorMessage());
_bstr_t bstrSource(e.Source());
_bstr_t bstrDescription(e.Description());
printf("\a\tSource = %s\n", (LPCSTR) bstrSource);
printf("\a\tDescription = %s\n", (LPCSTR) bstrDescription);
}
Read more about this code example here.