Вступление
JSON сейчас встречается буквально везде - от веб-сервисов до IoT, но есть нюанс: почти все популярные JSON-библиотеки писались с расчётом на десктопы и серверы, где оперативку никто не считает по килобайтам. А вот на микроконтроллерах, особенно Cortex-M, каждый байт на счету. Да, конечно, можно гонять сырые структуры и их же писать в конфиг-файл, это как говорится "не запрещено конституцией". Но отладка в таком случае превращается в отдельный квест. В какой-то момент я понял, что мне надоело вручную возиться с JSON на микроконтроллерах: писать километры кода для обхода дерева cJSON, ловить утечки и гадать, где malloc снова подставит. Из всех этих соображений и родился JsonX — лёгкая и минималистичная надстройка над cJSON, которую я делал в первую очередь под микроконтроллеры.
Преимущество JsonX:
удобный маппинг JSON <> C-структуры через плоское описание
JX_ELEMENT[]возможность работать полностью без динамических аллокаций (baremetal) или через системные аллокаторы (ThreadX / FreeRTOS)
простая конфигурация через jx_config.h
и, самое важное, простая сериализация / десериализация C-структур
Основная идея JsonX
Хотя cJSON внутри парсит JSON в классическое дерево указателей, JsonX дополняет этот процесс плоским массивом элементов JX_ELEMENT[], который используется для автоматического маппинга на поля вашей структуры. Схема данных JX_ELEMENT[] описывается плоско и заранее.
typedef struct
{
char property[JX_PROPERTY_MAX_SIZE];
JX_ELEMENT_TYPE type;
uint8_t value_len;
const void value_p;
JX_ELEMENT_STATUS status;
struct json_element_s element;
uint16_t element_size;
} JX_ELEMENT;
Это нам даёт:
Контролируемая работа с памятью: хотя cJSON внутри всё ещё строит дерево и делает аллокации, JsonX изолирует их в заданном аллокаторе и гарантирует корректное освобождение без утечек.
Пример использования
Структура конфигурации:
typedef struct
{
char device_name[32];
uint32_t baudrate;
bool debug;
} config_t;
config_t config;
Описание маппинга:
JX_ELEMENT config_desc[] =
{
JX_PROPERTY_STRING("device_name", config.device_name),
JX_PROPERTY_NUMBER("baudrate", config.baudrate),
JX_PROPERTY_BOOLEAN("debug", config.debug),
};
const size_t config_desc_size = sizeof(config_desc)/sizeof(config_desc[0]);
Парсинг JSON:
const char *config_json_str = "{\"device_name\":\"sensor1\",\"baudrate\":9600,\"debug\":true}";
if(jx_json_to_struct(char *buffer, config_desc, config_desc_size, JX_MODE_STRICT) == JX_SUCCESS)
{
jx_log("Конфиг загружен: %s @ %lu\n", config.device_name, config.baudrate);
}
Генерация JSON:
char buffer[256];
jx_struct_to_json(config_desc, config_desc_size, buffer, sizeof(buffer), JX_FORMATTED);
jx_log("config: %s\r\n", buffer);
Гибкая конфигурация под проект
JsonX можно собрать в одном из нескольких режимов:
ThreadX / FreeRTOS - с использованием системных аллокаторов
Baremetal - с собственным статическим пулом
Custom Allocators - использование полностью пользовательских функций выделения/освобождения памяти, переданных в JsonX (например, для POSIX или специфичных RTOS-аллокаторов)
Всё настраивается через jx_config.h и флаги компиляции.
Ограничения
Все числа - пока double (как у cJSON). Планируется режим strict int
Максимальный размер имени поля задаётся макросом JX_PROPERTY_MAX_SIZE
Нет автоматического увеличения массива элементов - лимит задаётся заранее.
Планы на развитие
В текущей версии JsonX использует cJSON как движок парсинга и генерации. Однако в перспективе я рассматриваю вариант полного отказа от cJSON в пользу собственного парсера, который:
будет работать напрямую с заданным пулом памяти (например, block pool в ThreadX)
получит безопасный аллокатор для FreeRTOS с контролем границ и предотвращением фрагментации
сможет работать в полностью статическом режиме без единого вызова malloc/free
Снижение накладных расходов и позволит полностью исключить динамику при работе с JSON, если это критично для проекта
Окончательное решение будет зависеть от того, насколько библиотека будет востребована в сообществе и какие требования будут у пользователей. Если спрос на такую функциональность окажется высоким - развитие пойдёт в эту сторону
Заключение
JsonX - это способ использовать cJSON в микроконтроллерах без непредсказуемых аллокаций и лишнего кода. Он особенно полезен там, где важно контролировать каждый байт и каждую миллисекунду, но при этом нужно работать с JSON так же удобно, как на ПК.
Репозиторий: JsonX
Примеры - в файле: src/example.c
Комментарии (0)

zurabob
18.09.2025 12:31JSMN (минималистичный парсер на C)
У меня подозрение, что автор вдохновлялся именно им, я тоже к нему добавлял парсинг чисел, true/false и генерацию json. Очень приятная и небольшая заготовка для самодеятельности.

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

Indemsys
18.09.2025 12:31Непонятно что тут означает "контролируемая работа с памятью".
Если есть неизвестный JSON в котором надо что-то выловить, то парсить дерево нужно. И без динамической памяти никак. И откуда там утечки если библиотека протестирована.
Динамическая память - это всегда фрагментация, и риск потери чистой памяти.
Но проблема решается элементарно просто наложением мьютека на выделение памяти другими задачами на время парсинга JSON.
Я бы конечно рекомендовал https://github.com/akheron/jansson
В современной реальности самые лучшие библиотеке - это те что понятны и доступны для кодирующих агентов.
Так вот с jansson агент Claude работает безукоризненно. Может построить парсигн и генерацию любого дерева. Делает практически безошибочно. Это то, что агенты с самого начала научились хорошо делать.
mihailasd Автор
18.09.2025 12:31Под "контролируемой работой с памятью" я имел в виду не то, что JsonX волшебным образом убирает динамику, а то, что все выделения идут через заранее определённый механизм. Это может быть пул в ThreadX, безопасный аллокатор для FreeRTOS или статический буфер в baremetal. То есть разработчик сам решает, откуда брать память, и она не "разъезжается" по куче.
Да, если JSON заранее неизвестной структуры, то дерево всё равно строится. Но JsonX позволяет хотя бы держать это дерево в предсказуемых границах, и освобождается оно единым вызовом — без риска потерять куски.
Согласен, что мьютекс решает вопросы многопоточности при malloc. Но на длинных аптаймах фрагментация и непредсказуемые провалы при аллокации могут быть куда болезненнее, чем гонки за mutex. В enterprise-системах это особенно критично, и именно поэтому я старался уйти от кучи и сделать работу предсказуемой.
Jansson знаю и сам пробовал - отличная библиотека, но она работает только с деревом JSON-объектов. Автоматического маппинга в структуры у неё нет: значения всё равно приходится доставать вручную и переносить в поля. JsonX же изначально задумывался именно под MCU и RTOS, где важны минимализм, предсказуемость и возможность описать схему один раз через
JX_ELEMENT[], а дальше работать со структурами напрямую.Все эти преимущества сложно оценить, если вы сами не сталкивались с ограничениями MCU и RTOS, где каждая утечка или лишний malloc могут стоить стабильности системы.

Indemsys
18.09.2025 12:31Автоматический папинг делает кодирующий агент, я же написал.
Агенты это делают превосходно.
А у вас псевдоавтоматизм. Поскольку структуру JSON надо знать заранее.
mihailasd Автор
18.09.2025 12:31Автоматический mapping в JsonX это не "псевдоавтоматизм", а практический инструмент. Да, структура JSON должна быть известна заранее, и это абсолютно нормально для embedded-конфигов и отчётов: там всегда есть фиксированная схема. В таких случаях
JX_ELEMENT[]позволяет один раз описать поля и больше не думать, как руками вытаскивать данные из дерева.
Когда JSON реально неизвестной структуры - тогда, конечно, остаётся классический обход дерева. Но в индустриальных MCU/RTOS-проектах это скорее редкость: чаще работают с предсказуемыми форматами обмена, где как раз и нужен быстрый и безопасный маппинг. То есть JsonX и задумывался не как универсальный агент, а как практичное решение для конкретной задачи: работа с фиксированными JSON-схемами на микроконтроллерах с жёсткими ограничениями по памяти.Claude и другие LLM-агенты действительно могут сымитировать парсинг/генерацию JSON или даже сгенерировать код для этого - у них в обучении было море примеров. Но это инструмент для разработчика, а не для встраиваемой системы.
Эта библиотека не "понятна и доступна" для Claude по одной простой причине: у неё нет STL, шаблонов и типичных C++-механизмов, к которым модели привыкли. Это чистый C с ручным маппингом черезJX_ELEMENT[]. Для агента это не очевидно: попробуйте скормить Claude саму либу и попросить сгенерировать пример работы - он скорее всего запутается в описании маппинга или выдаст "обычный" код на cJSON. JsonX как раз и нужен для того, чтобы убрать эту рутину на MCU.
Indemsys
18.09.2025 12:31Извините, не знал что вы разработчик всех "индустриальных 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++-механизмы"

mihailasd Автор
18.09.2025 12:31Если формат JSON меняется, код всё равно приходится обновлять. Разница лишь в подходе:
с jansson это новый сгенерированный/написанный код парсинга,
с JsonX — просто правка схемы JX_ELEMENT[] и точек, где реально используются новые поля.
То есть в JsonX меняешь декларативный шаблон, а не километры кода (пусть даже сгенерированного). Да и в git-коммитах это выглядит чище.
JsonX ещё добавляет толерантность к изменениям:
неизвестные ключи игнорируются автоматически
поля можно объявить optional с дефолтами
легко держать две версии схемы и выбирать их по version внутри JSON.
Это снижает риск сломаться при обновлении формата и делает цикл обновил -> пересобрал -> перепрошил короче и предсказуемее.
Повторное использование кода при множестве схем
В JsonX ядро парсинга/генерации общее: новые структуры = новые таблицы маппинга, без дублирования логики.
В jansson (и подобных) для каждой схемы нужен свой код: либо ручной обход, либо отдельный десериализатор. В итоге каждая новая структура тянет за собой новый кусок боевого кода, который потом надо поддерживать.Поэтому я и считаю, что JsonX экономит ресурсы: вместо того чтобы писать новый десериализатор, я переиспользую существующий механизм и просто добавляю схему.

Indemsys
18.09.2025 12:31Тут вы просто противоречите повсеместному эмпирическому опыту.
Свой код от Claude я тоже могу объявить библиотекой и сказать какие у него незаменимые фичи типа: "поля можно объявить optional с дефолтами". Он ракетой такие либы умеет делать. Хуже. Что ни попроси, он всегда накидывает лишнего API, чтобы сразу полная либа получилась.
Вообще, где доказательства, что ваша либа не от Claude или чего-то подобного?
Бросайте это. Либы на коленке в ближайшие тысячу лет никому не будут нужны.
Лучше опубликуйте полезное приложение с применением JSON. Вот тогда будет понятно, что имеем дело с человеческим трудом.

graphican
18.09.2025 12:31Без фактов не интересно:
Сколько код бинарно занимает
Сколько на стеке и в куче памяти расходует
Время исполнения

Indemsys
18.09.2025 12:31Код 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. Но это контролируется, потому что глубину разбора задает макрос.
За скорость не скажу, там главный тормоз - медианоситель.

mihailasd Автор
18.09.2025 12:31JsonX по фактам:
Размер кода: сам 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 и утечек.

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

mihailasd Автор
18.09.2025 12:31Rust с serde - действительно мощная связка, но она уместна там, где есть ресурсы и готовность строить весь проект вокруг Rust. На embedded-рынке это пока очень нишевая история. Большинство реальных промышленных проектов до сих пор пишется на C, и часто в условиях, где Rust просто не вариант (отсутствие toolchain’а, строгие требования к сертификации, ограниченные MCU).
JsonX как раз и родился из практики: "староверы на C" сталкиваются не с отсутствием JSON, а с болью интеграции его в RTOS-среды с контролем памяти. И тут как раз нужна лёгкая обёртка, а не «своя кривоватая реализация».

Gordon01
18.09.2025 12:31где есть ресурсы и готовность строить весь проект вокруг Rust
Да, Россия - технологически отстающая страна, мы знаем. Только раст, его тулчейны и тем более сертификация тут вообще не при чем.
Большинство реальных промышленных проектов до сих пор пишется на C
Citation needed. В тех же США начиная со следующего года это законодательно запрещено.
JsonX как раз и родился из практики: "староверы на C" сталкиваются не с отсутствием JSON, а с болью интеграции его в RTOS-среды с контролем памяти
Не понимаю про что вы говорите. Эмбеддед - это такой же обычный си, в большинстве случаев собираемый мейнлайновым gcc и остальным гнутым тулчейном. Разница только в реализации принтов, флотов, да аллокатора, которые поддельные из-за того что си никогда не проектировался под эмбеддед, и получились костыли.
cJSON который вы оборачиваете дёргает аллокатор, работа которого на МК нежелательна.
Никогда прошивка с аллокацией на каждый чих не пройдёт никакую строгую сертификацию, так что непонятно зачем вы про серты вообще упоминаете.

mihailasd Автор
18.09.2025 12:31Citation needed. В тех же США начиная со следующего года это законодательно запрещено.
Вы прям заинтриговали меня. А можно ссылочку на закон, где это официально прописано?
Если сможете, буду признателен, интересно разобраться.

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

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

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

mihailasd Автор
18.09.2025 12:31Да, вы правы - если смотреть только на экономию памяти, то дешевле всего гнать сырые структуры и напрямую писать/читать их из конфигов. Я об этом и упоминал во введении: это действительно самый лёгкий путь, не запрещено конституцией.
Но на практике это не всегда удобно:Отладка превращается в мучение, особенно когда структур много и они громоздкие. С JSON проще открыть лог, быстро глазами проверить поле или прогнать через любой редактор.
Масштабируемость: когда проект растёт и структур десятки, ручное копание в бинарных дампах начинает тормозить разработку.
JsonX родился именно как компромисс: память контролируется, но при этом сохраняется удобство работы с читаемым JSON и автоматический маппинг в структуры.
А в итоге всё упирается в задачи: кому-то проще битовые пакеты, кому-то Protobuf, кому-то JSON. Тут как говорится, на вкус и цвет все фломастеры разные.
ptr128
18.09.2025 12:31Как раз сырые структуры передавать не лучшая идея. Мало того, что они занимают больше места, чем PB, так еще и не поддерживают совместимость снизу вверх
ручное копание в бинарных дампах
Есть множество инструментов, которые позволяют просматривать и редактировать бинарную информацию в удобном для человека виде. Не поверите, но даже в процессе вебсерфинга это и происходит, так как обмен между браузером и сервером происходит преимущественно в бинарном виде после сжатия bzip.

mihailasd Автор
18.09.2025 12:31Согласен, PB выигрывает и по плотности, и по совместимости это его сильная сторона. Если главная цель именно максимальная экономия трафика и байтов в хранилище, то тут спорить нечего, PB/nanopb логичнее.
Но JsonX решает другую боль:
не экономия ради экономии, а предсказуемая работа в ограниченной среде (RTOS, MCU без динамики, фиксированные схемы);
JSON остаётся читаемым и легко отлаживаемым прямо в логах или редакторе, без генерации .proto и тулов;
автоматический маппинг в структуры на C даёт быстрый результат без тонны ручного кода.
Поэтому я и позиционирую JsonX как рабочий компромисс: в закрытых протоколах удобнее битовые пакеты или PB, но как только нужна интеграция с внешними сервисами или читаемость на этапе отладки - JSON выигрывает.
В итоге всё сводится к тому, что инструмент подбирается под задачу: где-то PB, где-то JSON, где-то вообще свой бинарный формат.

ptr128
18.09.2025 12:31предсказуемая работа
Предсказуемая? Если в 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 сделать затруднительно.

mihailasd Автор
18.09.2025 12:31Мы начали разговор про экономию памяти на MCU, а закончили обсуждением SOAP и enterprise-схем, это уже про совсем другой уровень задач.
Мы тут всё-таки обсуждаем подход к JSON на микроконтроллерах. Сравнивать JSON c SOAP или gRPC - это уже другой класс задач: обмен в корпоративных системах и B2B. Там свои требования, но это не отменяет того факта, что на MCU JSON остаётся самым простым и читаемым вариантом.
Вопрос "какой протокол лучше для enterprise" выходит за рамки - здесь речь идёт о том, как комфортно жить с JSON именно на контроллерах, где malloc/free превращаются в проблему, а разработчикам нужны удобные инструменты.
ptr128
18.09.2025 12:31Мы начали разговор про экономию памяти на 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, забив на расход памяти?

mihailasd Автор
18.09.2025 12:31Да, PB и SOAP отлично закрывают B2B-интеграции, в этом спору нет. Но статья изначально была не про это.
JsonX я задумывал именно как инструмент для локальной работы с JSON на MCU/RTOS, где формат уже выбран внешней системой или протоколом. Там задача не выбрать самый плотный формат передачи, а сделать жизнь разработчика проще: убрать боль с malloc/free, не писать тонну кода руками и при этом сохранить читаемость данных на этапе отладки.
Если проекту нужен Modbus или PB это правильный выбор для этого проекта. Но когда в системе уже есть JSON (а таких кейсов полно: конфиги, отчёты, API-шлюзы), то JsonX позволяет работать с ним предсказуемо и без геморроя на микроконтроллере.

ptr128
18.09.2025 12:31где формат уже выбран внешней системой или протоколом
Так с этого надо было и начинать. В таком случае, честь и хвала Вам за полезный труд.

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

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

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

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

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

mihailasd Автор
18.09.2025 12:31Вот как раз эта боль и была одной из причин появления JsonX.
cJSON сам по себе всегда строит полное дерево и делает динамические аллокации. На больших JSON это действительно может упереться в потолок кучи на ESP32 или MCU с меньшими ресурсами.
JsonX эту проблему не убирает магией, но даёт контроль. Все аллокации идут через заданный механизм (ThreadX block pool, кастомный аллокатор для FreeRTOS или статический буфер в baremetal). Вы сами решаете, откуда брать память, и гарантированно освобождаете всё одним вызовом.То есть если у вас формат заранее известен, можно ограничить использование кучи или полностью уйти от неё. А если JSON реально непредсказуемый и огромный, тогда да, придётся парсить дерево и держать его в памяти, тут JsonX тоже не спасёт.
nikolz
Что скажите про эти библиотеки:
--------------------------------------
Рекомендации DeepSeek :
1. ArduinoJson (самая популярная)
Плюсы: Легковесная, простая в использовании, отличная документация
Поддерживаемые архитектуры: AVR, ARM, ESP8266, ESP32
Особенности: Поддержка как десериализации, так и сериализации
cpp
2. jansson (порт для embedded)
Плюсы: Полнофункциональная, стандартный API
Минусы: Требует больше памяти
Лучше подходит для: Более мощных микроконтроллеров (ESP32, STM32)
3. cJSON
Плюсы: Минималистичная, переносимая, только C
Размер: ~10KB ROM
Поддержка: ARM, AVR, ESP
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
Рекомендации по выбору:
Для Arduino/ESP: ArduinoJson (идеальное сочетание удобства и ресурсов).
Для маломощных МК (ATtiny, AVR): JSMN (только парсинг) или cJSON.
Для STM32/ESP32 без Arduino: cJSON (C) или PicoJSON (C++).
Для сетевых проектов: Mongoose OS (если нужна интеграция с MQTT/HTTP).
Для мощных микроконтроллеров (ESP32, Teensy 4.x): nlohmann/json (если хватает оперативной памяти).
Все библиотеки, кроме nlohmann/json, подходят для устройств с оперативной памятью от 2 КБ. Для проектов с критичным потреблением памяти выбирайте JSMN или cJSON.
mihailasd Автор
Спасибо за ваш интерес и детальный обзор других JSON-библиотек.
Да, большинство библиотек из списка я знаю и пробовал. ArduinoJson, JSMN, jansson - все они хороши в своих нишах, ArduinoJson например действительно очень удобна на Arduino/ESP, у нее шикарная документация и примеры.
Но JsonX решает чуть другую задачу:
не пытается конкурировать с ArduinoJson или jansson, а именно дополняет cJSON.
Я делал JsonX в первую очередь для проектов на STM32 + RTOS, где хотелось иметь жесткий контроль над памятью (пул ThreadX, кастомные аллокаторы для FreeRTOS, статический буфер в baremetal). Это даёт предсказуемость и стабильность.
Многие популярные JSON-библиотеки делают ставку на C++ и естественно используют кучу. В C++ реализовать маппинг JSON структура относительно просто: есть шаблоны, STL и прочая "тяжёлая артиллерия".
А вот в чистом C это боль и грабли с топором на конце. Если библиотека написана на C, то: как правило, она ограничивается только парсингом JSON. Дальше разработчику приходится вручную обходить дерево или токены и самому переносить данные в структуру. JsonX решает эту проблему: он даёт именно автоматический маппинг JSON <> C-структуры через
JX_ELEMENT[]. То есть схема описывается один раз, а дальше можно работать напрямую со структурами, без километров шаблонного кода.Ivan_shev
ты так старательно собирал информацию, что на момент заговорил на китайском :)