Parsing Responses

parsers.h is an umbrella over lwip/parsers/*.h. The parsers work on any contiguous buffer; you do not need the network stack running to use them. They avoid heap allocation and use caller-supplied buffers throughout.

Include parsers.h (or the individual parser headers from lwip/parsers/) — you still need to call lwip_start() even if you only want the parsers, because the library must load its exports.

Note

For programs that only use the crypto or parser APIs without networking, call lwip_start() but skip lwip_network_up().

JSON

The JSON parser is cursor-based over a complete in-memory response body. json_next() advances a cursor and returns one token at a time. Objects and arrays come back as a single token whose value span covers the interior content; the cursor has already moved past the closing delimiter. Descend into an object or array with json_enter(); to skip a container, just do not call it — the parent cursor is already past it.

Parse a flat object:

#include <parsers.h>

static const char body[] =
    "{\"token_type\":\"Bearer\",\"expires_in\":3600}";

json_parser_t root, obj;
json_token_t tok;
char type_buf[32];
long expires;

json_init(&root, body, sizeof(body) - 1);
if (json_next(&root, &tok) == JSON_OK && tok.type == JSON_TOK_OBJECT) {
    json_enter(&obj, &tok);
    json_get_string(&obj, "token_type", type_buf, sizeof(type_buf));
    json_get_number(&obj, "expires_in", &expires);
}

Walk an array and descend into each element:

json_parser_t root, arr, item;
json_token_t tok;

json_init(&root, buf, len);
json_next(&root, &tok);           /* JSON_TOK_ARRAY */
json_enter(&arr, &tok);

while (json_next(&arr, &tok) == JSON_OK) {
    if (tok.type != JSON_TOK_OBJECT) continue;
    json_enter(&item, &tok);      /* descend into this element */
    /* search item with json_get_string / json_get_key_value */
    /* previous elements are already past in &arr — no skip needed */
}

XML

The XML parser is streaming and SAX-style. Feed bytes with xml_take(), call xml_finish() after the final byte, then pull events with xml_next(). Comments and processing instructions are skipped automatically. Event names, text, and attributes are copied into xml_event_t so you do not need to hold on to the original input buffer after feeding.

XML_FLAG_LAX enables HTML-tolerant mode: lowercase tag names, unquoted attributes, boolean attributes, and auto-closed void elements.

#include <parsers.h>

xml_ctx_t x;
xml_event_t evt;
char ring[512];
char title[64], id_buf[8];

xml_init(&x, ring, sizeof(ring), 0);
xml_take(&x, buf, len);
xml_finish(&x);

while (xml_next(&x, &evt) == XML_OK) {
    if (evt.type != XML_EVT_ELEMENT_START) continue;
    if (strcmp(evt.name, "item") == 0) {
        xml_get_attr(&evt, "id", id_buf, sizeof(id_buf));
    } else if (strcmp(evt.name, "title") == 0) {
        xml_get_inner_text(&x, title, sizeof(title));
    }
}

The ring buffer size determines how much unprocessed input the parser can hold at once. Size it to fit the largest single element you expect to see.

URL Encoding

url_build_query() constructs an application/x-www-form-urlencoded body from parallel key and value arrays. Percent-encoding follows RFC 3986.

#include <parsers.h>

char query[256];
const char *keys[]   = {"grant_type", "client_id"};
const char *values[] = {"client_credentials", "myapp"};
url_build_query(query, sizeof(query), keys, values, 2);
/* query == "grant_type=client_credentials&client_id=myapp" */

Individual percent-encode and percent-decode helpers are in lwip/parsers/url.h for use with raw URI components.