Примеры работы с JSON
Готовые примеры парсинга, создания и сериализации JSON-документов. Полное описание функций — в разделе JSON.
Парсинг строки
json_parse принимает строку и возвращает готовый документ. Корневой токен доступен через json_root, а тип проверяется функциями json_is_*:
// handlers/jsonpage.c
#include "http.h"
#include "json.h"
void parse_json(httpctx_t* ctx) {
const char* json_string =
"{\"name\":\"alex\",\"age\":30,\"active\":true,\"balance\":165.23}";
json_doc_t* doc = json_parse(json_string);
if (!doc) {
ctx->response->send_data(ctx->response, "json parse error");
return;
}
json_token_t* object = json_root(doc);
if (!json_is_object(object)) {
ctx->response->send_data(ctx->response, "root is not an object");
json_free(doc);
return;
}
json_token_t* name = json_object_get(object, "name");
if (name) {
ctx->response->send_data(ctx->response, json_string(name));
}
json_free(doc);
}Создание документа с нуля
Удобная точка входа — json_root_create_object (или json_root_create_array), которая создаёт документ и сразу назначает корневой токен. Токены создаются без документа: json_create_string, json_create_number, json_create_bool, json_create_null, json_create_object, json_create_array:
#include "http.h"
#include "json.h"
void build_json(httpctx_t* ctx) {
json_doc_t* doc = json_root_create_object();
json_token_t* object = json_root(doc);
json_object_set(object, "name", json_create_string("alex"));
json_object_set(object, "age", json_create_number(30));
json_object_set(object, "active", json_create_bool(1));
json_object_set(object, "balance", json_create_number(165.23));
json_object_set(object, "note", json_create_null());
ctx->response->add_header(ctx->response, "Content-Type", "application/json");
ctx->response->send_data(ctx->response, json_stringify(doc));
json_free(doc);
}Можно начать и с пустого документа, а корень назначить отдельно через json_set_root:
json_doc_t* doc = json_create_empty();
json_token_t* object = json_create_object();
json_set_root(doc, object);Вложенные объекты и массивы
Токены-контейнеры можно свободно вкладывать друг в друга. Значения создаются заранее и передаются в json_object_set / json_array_append:
#include "http.h"
#include "json.h"
void nested_json(httpctx_t* ctx) {
json_doc_t* doc = json_root_create_object();
json_token_t* root = json_root(doc);
// Вложенный объект "address"
json_token_t* address = json_create_object();
json_object_set(address, "city", json_create_string("Berlin"));
json_object_set(address, "zip", json_create_number(10115));
json_object_set(root, "address", address);
// Массив "tags" с prepend/append
json_token_t* tags = json_create_array();
json_object_set(root, "tags", tags);
json_array_append(tags, json_create_string("php"));
json_array_append(tags, json_create_string("c"));
json_array_prepend(tags, json_create_string("first"));
json_array_append_to(tags, 1, json_create_string("sql")); // вставка по индексу
// Изменение значения по индексу
json_token_t* second = json_array_get(tags, 1);
if (second) {
json_token_set_string(second, "rust");
}
ctx->response->add_header(ctx->response, "Content-Type", "application/json");
ctx->response->send_data(ctx->response, json_stringify(doc));
json_free(doc);
}Чтение типизированных значений
Числовые геттеры json_int, json_uint, json_llong, json_double принимают выходной параметр int* ok — он равен нулю, если токен не является числом. Проверить тип помогает json_is_number (и другие json_is_*):
#include "http.h"
#include "json.h"
void read_numbers(httpctx_t* ctx) {
json_doc_t* doc = json_parse("{\"count\":42,\"price\":19.95,\"name\":\"alex\"}");
if (!doc) {
ctx->response->send_data(ctx->response, "parse error");
return;
}
json_token_t* object = json_root(doc);
char response[256];
json_token_t* count = json_object_get(object, "count");
if (count && json_is_number(count)) {
int ok = 0;
int value = json_int(count, &ok);
if (ok) {
snprintf(response, sizeof(response), "count = %d", value);
ctx->response->send_data(ctx->response, response);
}
}
json_free(doc);
}Строки и логические значения читаются напрямую — без параметра ok: json_string(token), json_bool(token). Длину строки в байтах возвращает json_string_size.
Обход объекта и массива
Единый итератор json_it_t работает и для объектов, и для массивов. Для объекта ключ доступен через json_it_key, значение — через json_it_value:
#include "http.h"
#include "json.h"
void iterate_json(httpctx_t* ctx) {
json_doc_t* doc = json_parse("{\"a\":1,\"b\":2,\"c\":3}");
if (!doc) {
ctx->response->send_data(ctx->response, "parse error");
return;
}
json_token_t* object = json_root(doc);
char response[512] = "";
for (json_it_t it = json_init_it(object); !json_end_it(&it); it = json_next_it(&it)) {
const char* key = (const char*)json_it_key(&it);
json_token_t* value = json_it_value(&it);
int ok = 0;
int num = json_int(value, &ok);
char line[64];
snprintf(line, sizeof(line), "%s=%d | ", key, ok ? num : 0);
strncat(response, line, sizeof(response) - strlen(response) - 1);
}
ctx->response->send_data(ctx->response, response);
json_free(doc);
}При обходе массива json_it_key возвращает NULL — есть только значения. Удалить текущий элемент на ходу можно функцией json_it_erase.
Сериализация и ответ
json_stringify возвращает строку, привязанную к документу, и освобождается вместе с ним через json_free. Флаг ascii_mode = 1 кодирует все символы вне ASCII как \uXXXX — удобно для логов и совместимости со старыми клиентами:
#include "http.h"
#include "json.h"
void stringify_json(httpctx_t* ctx) {
json_doc_t* doc = json_parse("[\"Привет 😅\", 42, true]");
if (!doc) {
ctx->response->send_data(ctx->response, "parse error");
return;
}
doc->ascii_mode = 1; // escape non-ASCII as \uXXXX
ctx->response->add_header(ctx->response, "Content-Type", "application/json");
ctx->response->send_data(ctx->response, json_stringify(doc));
json_free(doc);
}Если строку нужно пережить документ — используйте json_stringify_detach. Она возвращает отдельную копию, которую необходимо освободить вручную через free:
char* body = json_stringify_detach(doc);
if (body) {
ctx->response->add_header(ctx->response, "Content-Type", "application/json");
ctx->response->send_data(ctx->response, body);
free(body);
}
json_free(doc);