All streams
Search
Write a publication
Pull to refresh

Comments 38

Что скажите про эти библиотеки:

--------------------------------------

Рекомендации DeepSeek :

1. ArduinoJson (самая популярная)

  • Плюсы: Легковесная, простая в использовании, отличная документация

  • Поддерживаемые архитектуры: AVR, ARM, ESP8266, ESP32

  • Особенности: Поддержка как десериализации, так и сериализации

cpp

#include <ArduinoJson.h>

void setup() {
  StaticJsonDocument<200> doc;
  doc["sensor"] = "temperature";
  doc["value"] = 23.5;
  
  serializeJson(doc, Serial);
}

2. jansson (порт для embedded)

  • Плюсы: Полнофункциональная, стандартный API

  • Минусы: Требует больше памяти

  • Лучше подходит для: Более мощных микроконтроллеров (ESP32, STM32)

3. cJSON

  • Плюсы: Минималистичная, переносимая, только C

  • Размер: ~10KB ROM

  • Поддержка: ARM, AVR, ESP

#include "cJSON.h"

cJSON *root = cJSON_CreateObject();
cJSON_AddStringToObject(root, "name", "test");
cJSON_AddNumberToObject(root, "value", 42);

4. JsonParser

  • Плюсы: Очень легкая, только парсинг

  • Размер: ~2-4KB ROM

  • Лучше для: Только чтения JSON

5. Microjson (минималистичная)

  • Плюсы: Крайне малый размер, только парсинг

  • Размер: ~1.5KB ROM

Рекомендации по выбору:

  • Для AVR/ATmega: ArduinoJson или Microjson

  • Для ESP8266/ESP32: ArduinoJson или cJSON

  • Для STM32: ArduinoJson или jansson

  • Если нужно только парсить: JsonParser или Microjson

ArduinoJson обычно是最好的 выбор для большинства проектов благодаря балансу функциональности и эффективности использования памяти.

=============================

Рекомендации GLM:

Ниже представлен обзор популярных библиотек для работы с JSON на микроконтроллерах с учётом их особенностей и ограничений по ресурсам:

1. ArduinoJson (рекомендуется для Arduino/ESP)

  • Платформы: Arduino, ESP8266, ESP32, STM32 и др.

  • Особенности:

    • Оптимизировано для устройств с ограниченным объёмом памяти (ОЗУ/Флэш).

    • Поддерживает сериализацию (создание

    • Удобный API, интеграция с Arduino IDE

    • Автоматическое управление памятью (статический или динамический пул).

  • Пример:

    #include <ArduinoJson.h>

    void setup() {

    StaticJsonDocument<200> doc; // Выделяем 200 байт

    doc["sensor"] = "temperature";

    doc["value"] = 25.5;

    serializeJson(doc, Serial); // Вывод: {"sensor":"temperature","value":25.5}

    }

  • Ссылка: [arduinojson.org](https://arduino

2. cJSON (лёгкая библиотека на C)

  • Платформы: любые микроконтроллеры (STM32, AVR, ESP32 и др.).

  • Особенности:

    • Минималистичная (один файл: cJSON.c + cJSON.h).

    • Не требует динамической памяти (но может использовать malloc).

    • Поддерживает парсинг и генерацию JSON.

    • Низкое потребление RAM.

  • Пример:

    #include "cJSON.h"

    void parse_json(const char *json) {

    cJSON *root = cJSON_Parse(json);

    cJSON *value = cJSON_GetObjectItem(root, "value");

    printf("Value: %f\n", value->valuedouble);

    cJSON_Delete(root);

    }

  • Ссылка: GitHub — DaveGamble/cJSON

3. JSMN (минималистичный парсер на C)

  • Платформы: устройства с крайне ограниченным объёмом памяти (ATtiny, MSP430).

  • Особенности:

    • Только парсинг (без генерации JSON).

    • Не использует malloc (работает с буфером).

    • Очень маленький размер кода (~1 КБ).

    • Требует ручной обработки токенов.

  • Пример:

    #include "jsmn.h"

    void parse_json(const char *json) {

    jsmn_parser parser;

    jsmntok_t tokens[10];

    jsmn_init(&parser);

    int count = jsmn_parse(&parser, json, strlen(json), tokens, 10);

    // Обработка токенов вручную...

    }

  • Ссылка: GitHub — zserge/jsmn

4. Mongoose OS (сетевая библиотека с поддержкой JSON)

  • Платформы: ESP32, ESP8266, STM32.

  • Особенности:

    • Включает парсер JSON как часть сетевой библиотеки.

    • Поддержка MQTT, HTTP, WebSocket.

    • Требует больше ресурсов, чем cJSON/JSMN.

  • Пример:

    #include "mgos.h"

    void handler(struct mg_connection nc, int ev, void ev_data) {

    struct mg_http_message hm = (struct mg_http_message ) ev_data;

    double value = json_get_double(hm->body, "$.value");

    printf("Value: %f\n", value);

    }

  • Ссылка: cesanta.com/mongoose

5. nlohmann/json (для мощных микроконтроллеров)

  • Платформы: ESP32, STM32H7, Teensy 4.x.

  • Особенности:

    • Современный C++ API (удобный, но ресурсоёмкий).

    • Требуется C++11 и поддержка STL.

    • Не рекомендуется для маломощных микроконтроллеров (из-за высокого потребления оперативной памяти).

  • Пример:

    #include <nlohmann/json.hpp>

    void parse_json(const std::string &json) {

    auto doc = nlohmann::json::parse(json);

    float value = doc["value"];

    Serial.println(value);

    }

  • Ссылка: GitHub — nlohmann/json

6. PicoJSON (лёгкая альтернатива nlohmann)

  • Платформы: ESP32, STM32.

  • Особенности:

    • Компакт

    • Поддерживает парсинг и генерацию.

    • Меньше ресурсов, чем у nlohmann/json

  • Ссылка: [GitHub — kazuho/picojson](GitHub — kazuho/picojson

Рекомендации по выбору:

  1. Для Arduino/ESP: ArduinoJson (идеальное сочетание удобства и ресурсов).

  2. Для маломощных МК (ATtiny, AVR): JSMN (только парсинг) или cJSON.

  3. Для STM32/ESP32 без Arduino: cJSON (C) или PicoJSON (C++).

  4. Для сетевых проектов: Mongoose OS (если нужна интеграция с MQTT/HTTP).

  5. Для мощных микроконтроллеров (ESP32, Teensy 4.x): nlohmann/json (если хватает оперативной памяти).

Все библиотеки, кроме nlohmann/json, подходят для устройств с оперативной памятью от 2 КБ. Для проектов с критичным потреблением памяти выбирайте JSMN или cJSON.

Спасибо за ваш интерес и детальный обзор других JSON-библиотек.
Да, большинство библиотек из списка я знаю и пробовал. ArduinoJson, JSMN, jansson - все они хороши в своих нишах, ArduinoJson например действительно очень удобна на Arduino/ESP, у нее шикарная документация и примеры.
Но JsonX решает чуть другую задачу:

  1. не пытается конкурировать с ArduinoJson или jansson, а именно дополняет cJSON.

  2. Я делал JsonX в первую очередь для проектов на STM32 + RTOS, где хотелось иметь жесткий контроль над памятью (пул ThreadX, кастомные аллокаторы для FreeRTOS, статический буфер в baremetal). Это даёт предсказуемость и стабильность.

  3. Многие популярные JSON-библиотеки делают ставку на C++ и естественно используют кучу. В C++ реализовать маппинг JSON структура относительно просто: есть шаблоны, STL и прочая "тяжёлая артиллерия".
    А вот в чистом C это боль и грабли с топором на конце. Если библиотека написана на C, то: как правило, она ограничивается только парсингом JSON. Дальше разработчику приходится вручную обходить дерево или токены и самому переносить данные в структуру. JsonX решает эту проблему: он даёт именно автоматический маппинг JSON <> C-структуры через JX_ELEMENT[]. То есть схема описывается один раз, а дальше можно работать напрямую со структурами, без километров шаблонного кода.

ты так старательно собирал информацию, что на момент заговорил на китайском :)

  1. JSMN (минималистичный парсер на C)

У меня подозрение, что автор вдохновлялся именно им, я тоже к нему добавлял парсинг чисел, true/false и генерацию json. Очень приятная и небольшая заготовка для самодеятельности.

Да, JSMN тоже нравится, но в JsonX я сразу хотел уйти дальше простого парсинга и добавить маппинг структур + контроль памяти.

Непонятно что тут означает "контролируемая работа с памятью".
Если есть неизвестный JSON в котором надо что-то выловить, то парсить дерево нужно. И без динамической памяти никак. И откуда там утечки если библиотека протестирована.
Динамическая память - это всегда фрагментация, и риск потери чистой памяти.
Но проблема решается элементарно просто наложением мьютека на выделение памяти другими задачами на время парсинга JSON.
Я бы конечно рекомендовал https://github.com/akheron/jansson
В современной реальности самые лучшие библиотеке - это те что понятны и доступны для кодирующих агентов.
Так вот с jansson агент Claude работает безукоризненно. Может построить парсигн и генерацию любого дерева. Делает практически безошибочно. Это то, что агенты с самого начала научились хорошо делать.

Под "контролируемой работой с памятью" я имел в виду не то, что JsonX волшебным образом убирает динамику, а то, что все выделения идут через заранее определённый механизм. Это может быть пул в ThreadX, безопасный аллокатор для FreeRTOS или статический буфер в baremetal. То есть разработчик сам решает, откуда брать память, и она не "разъезжается" по куче.

Да, если JSON заранее неизвестной структуры, то дерево всё равно строится. Но JsonX позволяет хотя бы держать это дерево в предсказуемых границах, и освобождается оно единым вызовом — без риска потерять куски.

Согласен, что мьютекс решает вопросы многопоточности при malloc. Но на длинных аптаймах фрагментация и непредсказуемые провалы при аллокации могут быть куда болезненнее, чем гонки за mutex. В enterprise-системах это особенно критично, и именно поэтому я старался уйти от кучи и сделать работу предсказуемой.

Jansson знаю и сам пробовал - отличная библиотека, но она работает только с деревом JSON-объектов. Автоматического маппинга в структуры у неё нет: значения всё равно приходится доставать вручную и переносить в поля. JsonX же изначально задумывался именно под MCU и RTOS, где важны минимализм, предсказуемость и возможность описать схему один раз через JX_ELEMENT[], а дальше работать со структурами напрямую.

Все эти преимущества сложно оценить, если вы сами не сталкивались с ограничениями MCU и RTOS, где каждая утечка или лишний malloc могут стоить стабильности системы.

Автоматический папинг делает кодирующий агент, я же написал.
Агенты это делают превосходно.
А у вас псевдоавтоматизм. Поскольку структуру JSON надо знать заранее.

Автоматический mapping в JsonX это не "псевдоавтоматизм", а практический инструмент. Да, структура JSON должна быть известна заранее, и это абсолютно нормально для embedded-конфигов и отчётов: там всегда есть фиксированная схема. В таких случаях JX_ELEMENT[] позволяет один раз описать поля и больше не думать, как руками вытаскивать данные из дерева.
Когда JSON реально неизвестной структуры - тогда, конечно, остаётся классический обход дерева. Но в индустриальных MCU/RTOS-проектах это скорее редкость: чаще работают с предсказуемыми форматами обмена, где как раз и нужен быстрый и безопасный маппинг. То есть JsonX и задумывался не как универсальный агент, а как практичное решение для конкретной задачи: работа с фиксированными JSON-схемами на микроконтроллерах с жёсткими ограничениями по памяти.

Claude и другие LLM-агенты действительно могут сымитировать парсинг/генерацию JSON или даже сгенерировать код для этого - у них в обучении было море примеров. Но это инструмент для разработчика, а не для встраиваемой системы.
Эта библиотека не "понятна и доступна" для Claude по одной простой причине: у неё нет STL, шаблонов и типичных C++-механизмов, к которым модели привыкли. Это чистый C с ручным маппингом через JX_ELEMENT[]. Для агента это не очевидно: попробуйте скормить Claude саму либу и попросить сгенерировать пример работы - он скорее всего запутается в описании маппинга или выдаст "обычный" код на cJSON. JsonX как раз и нужен для того, чтобы убрать эту рутину на MCU.

Извините, не знал что вы разработчик всех "индустриальных MCU/RTOS-проектов".
Шучу.

В реальности есть постоянная проблема когда новая версия JSON не повторяет старую. У меня, скажем, в индустриальной системе JSON меняется каждую неделю! Почему я должен держаться за формат который устарел?

Так вот  jansson избавляет от многих хлопот.

Я пишу исключительно на голом С и вот десериализатор сгенеренный целиком Claude на основе API jansson - https://github.com/Indemsys/MC80_4DC/blob/main/src/Parameters/Parameters_deserializer.c
А еще есть сериализатор, и еще есть куча мест где идет парсинг JSON.

А у тому клоню, что нет нынче никакой необходимости создавать обертки над такими эффективными и прозрачными либами как jansson.
Claude создаст весь прикладной код на лету на нативном API и под любой вкус. Была бы ему либа эта вся задокументрирована.

А вот когда одна либа поверх другой, а та поверх третьей, вот тут вы реально стреляете себе в ногу. Раздуваете контекст, усложняете работу агентам и запутываете их. Потом остается только пенять на заточенность под "STL, шаблоны и типичные C++-механизмы"

Если формат JSON меняется, код всё равно приходится обновлять. Разница лишь в подходе:

с jansson это новый сгенерированный/написанный код парсинга,

с JsonX — просто правка схемы JX_ELEMENT[] и точек, где реально используются новые поля.

То есть в JsonX меняешь декларативный шаблон, а не километры кода (пусть даже сгенерированного). Да и в git-коммитах это выглядит чище.

JsonX ещё добавляет толерантность к изменениям:

  • неизвестные ключи игнорируются автоматически

  • поля можно объявить optional с дефолтами

легко держать две версии схемы и выбирать их по version внутри JSON.

Это снижает риск сломаться при обновлении формата и делает цикл обновил -> пересобрал -> перепрошил короче и предсказуемее.

Повторное использование кода при множестве схем

В JsonX ядро парсинга/генерации общее: новые структуры = новые таблицы маппинга, без дублирования логики.
В jansson (и подобных) для каждой схемы нужен свой код: либо ручной обход, либо отдельный десериализатор. В итоге каждая новая структура тянет за собой новый кусок боевого кода, который потом надо поддерживать.

Поэтому я и считаю, что JsonX экономит ресурсы: вместо того чтобы писать новый десериализатор, я переиспользую существующий механизм и просто добавляю схему.

Тут вы просто противоречите повсеместному эмпирическому опыту.

Свой код от Claude я тоже могу объявить библиотекой и сказать какие у него незаменимые фичи типа: "поля можно объявить optional с дефолтами". Он ракетой такие либы умеет делать. Хуже. Что ни попроси, он всегда накидывает лишнего API, чтобы сразу полная либа получилась.

Вообще, где доказательства, что ваша либа не от Claude или чего-то подобного?
Бросайте это. Либы на коленке в ближайшие тысячу лет никому не будут нужны.
Лучше опубликуйте полезное приложение с применением JSON. Вот тогда будет понятно, что имеем дело с человеческим трудом.

Без фактов не интересно:

  • Сколько код бинарно занимает

  • Сколько на стеке и в куче памяти расходует

  • Время исполнения

Код Jansson: ~5,3 КБ.

Глобальные данные / константы: ≈0 Б.

Куча: все JSON-объекты (json_object, json_array, строки и т.п.) аллоцируются через malloc. malloc надо портировать через свое API, поэтому тут свобода реализации. В ThreadX, например malloc умеет делать статистику и ожидать освобождения памяти заданное время.

Пример: пустой json_object ≈ 32–64 байта + хэш-таблица.

Каждая строка хранится отдельно (длина + копия текста). Сколько сырой JSON без пробелов занимает, столько памяти приблизительно и нужно будет

По стеку. Там рекурсивный парсинг. Одна функция разбора (parse_value/parse_object/parse_array) тянет локальные переменные порядка 16–32 байт. Глубина рекурсии = глубине вложенности JSON. Но это контролируется, потому что глубину разбора задает макрос.

За скорость не скажу, там главный тормоз - медианоситель.

JsonX по фактам:

  • Размер кода: сам JsonX + cJSON в сборке под STM32H7 занимают порядка 10–12 КБ в ROM + ваш mapping. JsonX добавляет буквально сотни байт сверху cJSON.

  • Память:
    В baremetal-режиме можно работать полностью без кучи, только со статическим пулом (размер задаёте сами).
    В RTOS-режиме (ThreadX/FreeRTOS) аллокации идут через системный или кастомный allocator. В типовом примере JSON-конфига на ~500 байт суммарные выделения - до 2-3 КБ во время парсинга, после jx_free() память полностью освобождается.

  • Стек: функции не используют больших временных буферов, нагрузка минимальна (~200–300 байт).

  • Время: на STM32H753 (Cortex-M7, 400 МГц) парсинг JSON-строки размером ~1 КБ занимает <1 мс, генерация сопоставимого по размеру JSON - тоже в районе миллисекунды.

JsonX не претендует на рекорды по скорости - это по сути thin-layer над cJSON. Основная цель: предсказуемость и контроль памяти, чтобы не было скрытых malloc/free и утечек.

Если спрос на такую функциональность окажется высоким - развитие пойдёт в эту сторону

Те, кто просветился и перешёл на json на МК уже давно пишут на Rust с serde. А староверам до вашего джейсона нет дела, а даже если есть - он не умеют подключать библиотеки и напишут свою кривую безаллокационную реализацию

Rust с serde - действительно мощная связка, но она уместна там, где есть ресурсы и готовность строить весь проект вокруг Rust. На embedded-рынке это пока очень нишевая история. Большинство реальных промышленных проектов до сих пор пишется на C, и часто в условиях, где Rust просто не вариант (отсутствие toolchain’а, строгие требования к сертификации, ограниченные MCU).

JsonX как раз и родился из практики: "староверы на C" сталкиваются не с отсутствием JSON, а с болью интеграции его в RTOS-среды с контролем памяти. И тут как раз нужна лёгкая обёртка, а не «своя кривоватая реализация».

где есть ресурсы и готовность строить весь проект вокруг Rust

Да, Россия - технологически отстающая страна, мы знаем. Только раст, его тулчейны и тем более сертификация тут вообще не при чем.

Большинство реальных промышленных проектов до сих пор пишется на C

Citation needed. В тех же США начиная со следующего года это законодательно запрещено.

JsonX как раз и родился из практики: "староверы на C" сталкиваются не с отсутствием JSON, а с болью интеграции его в RTOS-среды с контролем памяти

Не понимаю про что вы говорите. Эмбеддед - это такой же обычный си, в большинстве случаев собираемый мейнлайновым gcc и остальным гнутым тулчейном. Разница только в реализации принтов, флотов, да аллокатора, которые поддельные из-за того что си никогда не проектировался под эмбеддед, и получились костыли.

cJSON который вы оборачиваете дёргает аллокатор, работа которого на МК нежелательна.

Никогда прошивка с аллокацией на каждый чих не пройдёт никакую строгую сертификацию, так что непонятно зачем вы про серты вообще упоминаете.

Citation needed. В тех же США начиная со следующего года это законодательно запрещено.

Вы прям заинтриговали меня. А можно ссылочку на закон, где это официально прописано?

Если сможете, буду признателен, интересно разобраться.

  1. The development of new product lines for use in service of critical infrastructure or NCFs in a memory-unsafe language (e.g., C or C++) where readily available alternative memory-safe languages could be used is dangerous and significantly elevates risk to national security, national economic security, and national public health and safety.

For existing products written in memory-unsafe languages, not having a published memory safety roadmap is dangerous and significantly elevates risk to national security, national economic security, and national public health and safety. The memory safety roadmap should outline the manufacturer’s prioritized approach to eliminating memory safety vulnerabilities in priority code components written in memory unsafe languages (e.g., network-facing code or code that handles sensitive functions like cryptographic operations). Manufacturers should demonstrate that the memory safety roadmap will lead to a significant, prioritized reduction of memory safety vulnerabilities in the manufacturer’s products and demonstrate they are making a reasonable effort to follow the memory safety roadmap. Publication of a memory safety roadmap does not apply to products that have an announced end-of-support date that is prior to Jan. 1, 2030.

https://www.cisa.gov/resources-tools/resources/product-security-bad-practices#:~:text=manufacturers should avoid.-,Product Properties,safety vulnerabilities in priority code components written in memory unsafe languages.,-CWE (Common%20Weakness

Формулировки из CISA это рекомендации и требования по roadmap’у безопасности, а не закон. Да, сейчас есть тренд в США и Европе на уход от memory-unsafe языков, особенно в критической инфраструктуре. Но прямого запрета на C/C++ нет, и он вряд ли будет потому как слишком много кода, драйверов и ядер написано именно на C.

Если нужна экономия памяти, то не разумней ли использовать ProtocolBuffers и nanopd

Protocol Buffers/nanopb хороши, когда критична максимальная экономия байтов. Но у них минус - генерация кода и бинарный формат, который неудобно отлаживать. JsonX оставляет читаемый JSON и упрощает жизнь на MCU, где важен именно контроль памяти и маппинг в структуры.

Так про экономию памяти и статья. PB, особенно для числовых данных, даёт очень существенную экономию памяти - в разы. А уж при отладке можно потерпеть и бинарный формат. Благо все инструменты для работы с ним имеются.

Да, вы правы - если смотреть только на экономию памяти, то дешевле всего гнать сырые структуры и напрямую писать/читать их из конфигов. Я об этом и упоминал во введении: это действительно самый лёгкий путь, не запрещено конституцией.
Но на практике это не всегда удобно:

  • Отладка превращается в мучение, особенно когда структур много и они громоздкие. С JSON проще открыть лог, быстро глазами проверить поле или прогнать через любой редактор.

  • Масштабируемость: когда проект растёт и структур десятки, ручное копание в бинарных дампах начинает тормозить разработку.

JsonX родился именно как компромисс: память контролируется, но при этом сохраняется удобство работы с читаемым JSON и автоматический маппинг в структуры.
А в итоге всё упирается в задачи: кому-то проще битовые пакеты, кому-то Protobuf, кому-то JSON. Тут как говорится, на вкус и цвет все фломастеры разные.

Как раз сырые структуры передавать не лучшая идея. Мало того, что они занимают больше места, чем PB, так еще и не поддерживают совместимость снизу вверх

ручное копание в бинарных дампах

Есть множество инструментов, которые позволяют просматривать и редактировать бинарную информацию в удобном для человека виде. Не поверите, но даже в процессе вебсерфинга это и происходит, так как обмен между браузером и сервером происходит преимущественно в бинарном виде после сжатия bzip.

Согласен, PB выигрывает и по плотности, и по совместимости это его сильная сторона. Если главная цель именно максимальная экономия трафика и байтов в хранилище, то тут спорить нечего, PB/nanopb логичнее.

Но JsonX решает другую боль:

  • не экономия ради экономии, а предсказуемая работа в ограниченной среде (RTOS, MCU без динамики, фиксированные схемы);

  • JSON остаётся читаемым и легко отлаживаемым прямо в логах или редакторе, без генерации .proto и тулов;

  • автоматический маппинг в структуры на C даёт быстрый результат без тонны ручного кода.

Поэтому я и позиционирую JsonX как рабочий компромисс: в закрытых протоколах удобнее битовые пакеты или PB, но как только нужна интеграция с внешними сервисами или читаемость на этапе отладки - JSON выигрывает.

В итоге всё сводится к тому, что инструмент подбирается под задачу: где-то PB, где-то JSON, где-то вообще свой бинарный формат.

предсказуемая работа

Предсказуемая? Если в PB int32 никогда не превратится в пять миллиардов или вообще в дробное число, то JSON это ну никак не гарантирует. С типизацией у JSON всё очень плохо.

JSON остаётся читаемым и легко отлаживаемым прямо в логах или редакторе

Так как логи не в оперативной памяти хранятся, то пишите туда хоть CSV. Экономить там особо нечего. И в редакторе тоже никто не запрещает использовать JSON, который при сборке превратится в PB, если такое требуется. Хотя такой потребности у меня ни разу не возникало. Всё же PB - это протокол для обмена данными, а JSON - формат исходных файлов, где часто удобней TOML или YAML, который при сборке проекта при необходимости конвертируется в статическую структуру данных в флеш или EEPROM памяти MCU.

автоматический маппинг в структуры на C даёт быстрый результат без тонны ручного кода

А в PB что-ли иначе?

как только нужна интеграция с внешними сервисами или читаемость на этапе отладки - JSON выигрывает

Вы так шутите? JSON Schema так и не взлетела и до сих пор заметно уступает XSD. Почти весь B2B в enterprise так и сидит на SOAP. Потому что там часто надо точно указывать, что вот этот вес округляется до трех знаков после запятой, а вот эта цена - до двух, это поле может принимать только такие значения, это поле обязательное, а это - опциональное. А уж для MCU делать интеграцию с внешними, а не внутренними сервисами, да еще и заниматься разбором JSON Schema - очень странное решение.

На поставщиков информации в JSON мне приходилось нарываться. И это большая боль, так как парсить такой JSON и сохранять его в РСУБД - постоянный геморрой. Причем 9 из 10 таких поставщиков о JSON Schema и не слышали, публикуя описание схемы в текстовом формате где-то на своём сайте и меняя его без предупреждения.

А вот на PB реализовать внешний обмен получается, хотя и приходится обвешивать proto уточнениями вида [(goole.type.options).sql_type = "decimal(12,3)"]. Но это уже другая история, так как потоковый gRPC позволяет пушить уведомления, когда принимающая сторона за NAT, что на SOAP сделать затруднительно.

Мы начали разговор про экономию памяти на MCU, а закончили обсуждением SOAP и enterprise-схем, это уже про совсем другой уровень задач.
Мы тут всё-таки обсуждаем подход к JSON на микроконтроллерах. Сравнивать JSON c SOAP или gRPC - это уже другой класс задач: обмен в корпоративных системах и B2B. Там свои требования, но это не отменяет того факта, что на MCU JSON остаётся самым простым и читаемым вариантом.
Вопрос "какой протокол лучше для enterprise" выходит за рамки - здесь речь идёт о том, как комфортно жить с JSON именно на контроллерах, где malloc/free превращаются в проблему, а разработчикам нужны удобные инструменты.

Мы начали разговор про экономию памяти на MCU, а закончили обсуждением SOAP и enterprise-схем, это уже про совсем другой уровень задач.

А это кто писал?

нужна интеграция с внешними сервисами

Причем одно другому не помеха. Например - АСКУЭ, который с одной стороны, интеграция MCU с внешними сервисами, а с другой стороны - или бинарный протокол до концентраторов (например, DLMS\COSEM), и уже SOAP или PB до серверов в ЦОД.

Или берем ПЛК, которые тоже с текстовым JSON не дружат, но дружат с бинарным Modbus-TCP

gRPC - это уже другой класс задач

Только, для примера, тот же PLCNext без проблем gRPC поддерживает. Ну или если ближе DIY, то при помощи nanopd+nghttp2 gRPC вполне доступен на ESP32.

как комфортно жить с JSON именно на контроллерах, где malloc/free превращаются в проблему, а разработчикам нужны удобные инструменты

Я дал на него ответ, но Вы почему-то хотите в бинарной прошивке и оперативке MCU видеть текстовый формат JSON, даже не взирая на то, что памяти на него нужно больше в разы, чем для PB. Осталось еще локальные переменные в текстовом формате хранить, гоняя их через sscanf()/sprintf() )))

Я понимаю, что Modbus или CAN не всегда подходят, но сразу от них прыгать на JSON, забив на расход памяти?

Да, PB и SOAP отлично закрывают B2B-интеграции, в этом спору нет. Но статья изначально была не про это.

JsonX я задумывал именно как инструмент для локальной работы с JSON на MCU/RTOS, где формат уже выбран внешней системой или протоколом. Там задача не выбрать самый плотный формат передачи, а сделать жизнь разработчика проще: убрать боль с malloc/free, не писать тонну кода руками и при этом сохранить читаемость данных на этапе отладки.

Если проекту нужен Modbus или PB это правильный выбор для этого проекта. Но когда в системе уже есть JSON (а таких кейсов полно: конфиги, отчёты, API-шлюзы), то JsonX позволяет работать с ним предсказуемо и без геморроя на микроконтроллере.

где формат уже выбран внешней системой или протоколом

Так с этого надо было и начинать. В таком случае, честь и хвала Вам за полезный труд.

Интересная идея. А если мне нужны только некоторые поля файла, только их надо указать в маппинге? Остальные будут отброшены? И как указать в маппинге вложенные объекты и массивы?

Да, в маппинге JX_ELEMENT[] вы описываете только те поля, которые вам нужны. Если в JSON будут лишние ключи то они просто игнорируются.
С вложенными объектами и массивами схема похожая:
- Для объекта можно задать под-массив JX_ELEMENT[]и указать на него ссылку в описании. Тогда парсер зайдёт внутрь и распарсит только те поля, что вы описали.
- Для массивов можно задать фиксированный размер и тип элементов. Например, массив чисел из 5 элементов или массив структур с под-описанием.

Пример здесь в комментах посмотрите. Я постарался в самой либе включить все кейсы. Если есть пример JSON могу помочь с его маппингом.

Использую в микроконтроллерах JFES – если не ошибаюсь, развитие JSMN. На Хабре статью когда-то нашел про эту библиотеку стать.

Я тоже в своё время использовал JFES (и, кажется, тоже именно на Хабре впервые о ней узнал).
Библиотека действительно хорошая и лёгкая. Но у неё есть свой минус — нет маппинга: после парсинга всё равно приходится руками разбирать дерево/токены и переносить данные в структуру.
JsonX как раз родился как надстройка над cJSON именно ради этого: один раз описал схему JX_ELEMENT[] и дальше работаешь сразу со структурами, без километров кода для обхода.

Динамическая аллокация cJSON может больно укусить. Делал часы, там запрашивался прогноз погоды, в ответ приходил 16-20 КБ джейсон. На ESP32 иногда не хватало кучи, чтобы его распарсить в дерево. Читая описание подумал, что автор как-то решил проблему аллокаций...

Вот как раз эта боль и была одной из причин появления JsonX.
cJSON сам по себе всегда строит полное дерево и делает динамические аллокации. На больших JSON это действительно может упереться в потолок кучи на ESP32 или MCU с меньшими ресурсами.
JsonX эту проблему не убирает магией, но даёт контроль. Все аллокации идут через заданный механизм (ThreadX block pool, кастомный аллокатор для FreeRTOS или статический буфер в baremetal). Вы сами решаете, откуда брать память, и гарантированно освобождаете всё одним вызовом.

То есть если у вас формат заранее известен, можно ограничить использование кучи или полностью уйти от неё. А если JSON реально непредсказуемый и огромный, тогда да, придётся парсить дерево и держать его в памяти, тут JsonX тоже не спасёт.

Sign up to leave a comment.

Articles