Introduction
cojson is a C++ pull-type JSON parser/serializer for constrained platforms, such as bare metal applications on low-end MCUs. It does not use memory allocation and has almost no external dependencies. It is not intrusive – it neither forces nor implies any particular design of the application. Instead it adapts to fit any existing application code. It is tolerant to data type mismatching. When such occurs, parser just skips mismatching data and makes best efforts to continue parsing. The parser is recursive, e.g. nested JSON elements are handled with the recursion. However, this recursion is driven by the structure definition, not by the input data, which prevents stack faults on malformed input data.
cojson is character type neutral – it can work with signed or unsigned character, as well as with standard wide character types: wchar_t, char16_t and char32_t. It is also transparent for UTF8 and properly handles BOM sequence.
cojson works against a user-defined structure which specifies hierarchy, data types, and data storage access methods. Thus, when parsing is complete, the data already delivered to the application and no further processing needed. The same structure definition is also used for writing JSON. The structure is defined with a set of templetized function, for example, a PDO object with three members can be defined as simple as:
#include "cojson.hpp" using namespace cojson; #define NAME(s) static inline constexpr const char* s() noexcept { return #s; } class Pdo { struct Name { NAME(u) NAME(s) NAME(v) }; char s[16]; short u; short v; // JSON structure definition static const clas<Pdo>& structure() { return O<Pdo, P<Pdo, Name::s, countof(&Pdo::s), &Pdo::s>, P<Pdo, Name::u, decltype(Pdo::u), &Pdo::u>, P<Pdo, Name::v, decltype(Pdo::v), &Pdo::v> >(); } public: // reading JSON inline bool read(lexer& in) { return structure().read(*this,in); } // writing JSON inline bool write(ostream& out) { return structure().write(*this,out); } };
In this example, JSON structure is defined in a static function structure(), which uses cojson function O to define a JSON object, and P to define a JSON member. s, u, v – are static methods returning property names defined with the macro NAME. Methods read and write are given for illustration and convenience purposes.
Requirements
- Compiler: cojson sources need a C++11 enabled compiler, such as g++-4.8 and up.
- Library: libstdc++ v3 highly desirable. But if not available a workaround exists
- Code space: Depending on the platform and JSON structure complexity varies from 4kB to 20kB.
- RAM space: 20-80 bytes per entry in the defined JSON structure
Code Size Metrics
The slide below illustrates code size (in kilobytes) for metrics of different complexity and different target platforms.
Example above corresponds to metric 07-object-pdo.
Some notes for the slide:
- Code size is calculated as growth of the compiled metric binary after including the corresponding metric. It does not include standard platform library, such as long or double multiplications, string comparison, etc.
- Target PIC32MX is compiled without any optimization. It may give better results if optimization is enabled.
- Targets ATmega and PIC32MX have data type double equal to
float and, perhaps, therefore they show smaller code size than Cortex-M4
Speed Metrics
Table below contains some measurements made on Arduino Mega and Teensy 3.1. The complex object used in test contains 60 JSON entries and 33 of them are object members. Again, please notice performance with double – ATmega has it twice shorter and that makes significant difference.
MetricMCU @ FRQ (MHz) | Cortex-M4 @ 72 | ATmega @ 16 |
---|---|---|
r/w arrays of double (ms per 1000) | 58 | 38 |
reading complex object (ms per 1000) | 108 | 532 |
writing complex object (ms per 1000) | 230 | 272 |
Additional Materials
cojson tutorial
cojson on github
Other JSON parsers
List of JSON/CPP libs is available at: http://en.cppreference.com/w/cpp/links/libs#JSON
Solutions which are most close to cojson
Post a Comment