Исторически так сложилось, что JSON как текстовый формат используется в основном в веб, клиент-серверном обмене данными, межсерверном обмене данными и т.п., но никто не мешает его использовать и в embedded, тем более что я нашел достаточно много материалов по этой теме в интернете и смог разобраться, попробую здесь описать свой опыт использования JSON в программировании для модемов Quectel на OpenCPU.
Для начала небольшая справка.
Цитата из википедии:
JSON (JavaScript Object Notation) — текстовый формат обмена данными, основанный на JavaScript. Как и многие другие текстовые форматы, JSON легко читается людьми. За счёт своей лаконичности по сравнению с XML, формат JSON может быть более подходящим для сериализации сложных структур.
Так уж вышло, что OpenCPU написан полностью на Си, использовать Си++ и какие-нибудь библиотеки с многими зависимостями для JSON здесь не получится, да и ресурсы весьма ограничены, здесь вам не компьютер и памяти в том же модуле M66 выделяется всего 360kB для программы и 100kB RAM.
К слову, сама OpenCPU достаточно не дружелюбна к разработчику и при ее использовании вы будете натыкаться на непонятные моменты и очень часто никто вам не поможет, в интернете материалов почти нет, документация на английском и достаточно скудная, сама библиотека изобилует бинарниками и как работает та или иная функция зачастую приходится только догадываться. Но выбора для этих модулей просто нет и приходится использовать то, что есть.
Исходя из всего выше изложенного, я искал код для использования JSON по следующим критериям:
- Не должно быть практически никаких зависимостей;
- Написан на чистом Си;
- Должен легко портироваться под мое применение;
- Кроме парсера должна быть возможность формирования JSON объектов и сериализации.
И я нашел JFES — Json For Embedded Systems. Документация у этого проекта вся описана в README репозитория, прочитать ее минутное дело, я не буду здесь делать выкладки из нее. Остановлюсь на применении в OpenCPU.
Чтобы начать работать с JFES необходимо скопировать файлы jfes.c и jfes.h в папку /custom проекта Opencpu и подключить хэдер в файле main.c:
#include "jfes.h"
Чтобы инициализировать библиотеку необходимо инициализировать объект jfes_config_t:
jfes_config_t config;
Теперь самое важное JFES не умеет выделять память сам, поэтому необходимо указать функции для выделения и освобождения памяти, которые предоставляются Opencpu:
config.jfes_malloc = Ql_MEM_Alloc; config.jfes_free = Ql_MEM_Free;
На этом как бы все, инициализация библиотеки закончена и дальше уже можно парсить JSON. Попробуем простой пример для формирования следующего JSON для последующей отправки через GPRS:
{ "id": 12345, "imei": "759304218643261", "status": true }
Код будет выглядеть следующим образом:
jfes_value_t * value = jfes_create_object_value ( &config ); jfes_set_object_property ( &config, value, jfes_create_integer_value ( &config, 12345 ), "id", 0 ); jfes_set_object_property ( &config, value, jfes_create_string_value ( &config, "759304218643261", 0 ), "imei", 0 ); jfes_set_object_property ( &config, value, jfes_create_boolean_value ( &config, true ), "status", 0 );
При создании объекта строкового типа указывается последним аргументом длина строки или 0, если строка заканчивается нулевым символом.
Далее сериализуем полученный объект в строку для отправки. В dump_size будет лежать длина полученной строки:
char beauty_dump[1024]; jfes_size_t dump_size = 1024; jfes_value_to_string ( value, &beauty_dump[0], &dump_size, 1 );
Если последним параметром передать не 1, а 0, то вывод в строку будет одной строкой, без переносов, пробелов и т.п. (ugly json).
Полученная строка не будет нуль-терминированной, поэтому обязательно в конец необходимо добавить ноль:
beauty_dump[dump_size] = '\0';
Можно вывести полученный JSON в отладочный интерфейс:
APP_DEBUG ( "[ JSON ] %s \r\n", &beauty_dump[0] );
На этом все.