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");

}


21 Jan 2011

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?

21 Jan 2011

Yeah I coded something like this a couple months ago for an RSS parser.  I did everything a little different but same idea.  I often prefer to write my own stuff and just look at what others are doing for ideas.  This is better than my code I think :P

21 Jan 2011

user Cedric Martin wrote:

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?

You can just open a file normally, read from it and feed the data to the parser.

1 comment on XML parsing:

07 Aug 2012

Hi, this is some nice work. I'd like to try it out but get two compile errors saying: "cannot open source input file "TCPSocketConnection.h": No such file or directory" in file "HTTPClientHTTPClient.h", Line: 27, Col: 32

Please log in to post comments.