XML parsing
I have tried to port several C++ XML parsing libraries to the mbed platform (TinyXML, irrxml, rapidxml). Many of them work, but
- rapidxml only reads files (needs a FILE*)
- irrxml wants to have the XML file as a single char*
- tinyxml can do streaming reads, but needs a istream* object for that
I build a C++ stream class (to be exact a streambuf class) which can read data from a HTTPStream, but the C++ streams together with the whole networking stack and the C++ streams library takes so much memory that the programs either don't start at all or just stop working somewhere in the middle (the onliny compiler says that they take 73% RAM initially).
The only XML parser I found which can do streaming reads and is memory- friendly (from the mbed perspective) was spxml. The mbed library can be found here.
Usage
This program calls out to the google weather API (using the demo code for the HTTPStream class), parses the result and prints (to the debug console) the current temperature for LA. Since the original spxml library currently is only sparsely documented, you will need to read the *.hpp files for more information.
Use the SP_XmlHandle class, since it can retrieve child elements by name (which the normal element class cannot do). It also does proper null handling.
You can find the program here.
#include "mbed.h" #include "EthernetNetIf.h" #include "HTTPClient.h" #include "spdomparser.hpp" #include "spxmlnode.hpp" #include "spxmlhandle.hpp" EthernetNetIf eth; HTTPClient http; HTTPResult result; bool completed = false; void request_callback(HTTPResult r) { result = r; completed = true; } void parseWeather(SP_XmlElementNode *node) { SP_XmlHandle handle(node); SP_XmlElementNode * tempc = handle.getChild( "temp_c" ).toElement(); if (tempc) { printf("current temp=%sC\n",tempc->getAttrValue("data")); } SP_XmlElementNode * tempf = handle.getChild( "temp_f" ).toElement(); if (tempf) { printf("current temp=%sF\n",tempf->getAttrValue("data")); } } int main() { // the eth and HTTP code has be taken directly from the HTTPStream documentation page // see http://mbed.org/cookbook/HTTP-Client-Data-Containers printf("setup\n"); EthernetErr ethErr = eth.setup(); if (ethErr) { printf("Error in setup\n"); return -1; } printf("setup ok\n"); SP_XmlDomParser parser; HTTPStream stream; char BigBuf[512 + 1] = {0}; stream.readNext((byte*)BigBuf, 512); //Point to buffer for the first read HTTPResult r = http.get("http://www.google.com/ig/api?weather=Los+Angeles", &stream, request_callback); while (!completed) { Net::poll(); //Polls the Networking stack if (stream.readable()) { BigBuf[stream.readLen()] = 0; //Transform this buffer in a zero- terminated char* string parser.append( BigBuf, strlen(BigBuf)); // stream current buffer data to the XML parser stream.readNext((byte*)BigBuf, 512); //Buffer has been read, now we can put more data in it } } if (result == HTTP_OK) { printf("Read completely\n"); } else { printf("Error %d\n", result); return -1; } SP_XmlHandle rootHandle( parser.getDocument()->getRootElement() ); SP_XmlElementNode * child2 = rootHandle.getChild( "weather" ) .getChild( "current_conditions").toElement(); if ( child2 ) { parseWeather(child2); } if ( NULL != parser.getError() ) { printf( "\n\nerror: %s\n", parser.getError() ); } printf("end\n"); }
1 comment on XML parsing:
Please log in to post comments.
This is great,and works quite well! Although my trouble is that I have to parse a local file. Any idea on how I could do this easily using your example as a starting point?