From 64c257919e02b7ece4abe96af45bf99ba5791458 Mon Sep 17 00:00:00 2001 From: "Eric S. Raymond" Date: Wed, 17 Sep 2014 13:26:00 -0400 Subject: [PATCH] Beginnings of documentation, and a worked example. --- .gitignore | 1 + Makefile | 7 ++- example1.c | 35 +++++++++++++++ microjson.asc | 115 ++++++++++++++++++++++++++++++++++++++++++++++++++ microjson.c | 2 +- 5 files changed, 157 insertions(+), 3 deletions(-) create mode 100644 example1.c create mode 100644 microjson.asc diff --git a/.gitignore b/.gitignore index 30a9a25..b9b3be6 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ *.o test_microjson +example1 diff --git a/Makefile b/Makefile index 080cc42..025db30 100644 --- a/Makefile +++ b/Makefile @@ -15,15 +15,18 @@ test_microjson: test_microjson.o microjson.o check: test_microjson test_microjson +# Worked example +example1: example1.c microjson.c + clean: - rm -f microjson.o test_json + rm -f microjson.o test_json example1 CSUPPRESSIONS = -U__UNUSED__ cppcheck: cppcheck -I. --template gcc --enable=all --suppress=unusedStructMember $(CSUPPRESSIONS) *.[ch] SOURCES = Makefile *.[ch] -DOCS = README COPYING +DOCS = README COPYING microjson.asc ALL = $(SOURCES) $(DOCS) microjson-$(VERSION).tar.gz: $(ALL) tar --transform='s:^:microjson-$(VERSION)/:' --show-transformed-names -cvzf microjson-$(VERSION).tar.gz $(ALL) diff --git a/example1.c b/example1.c new file mode 100644 index 0000000..4dbd945 --- /dev/null +++ b/example1.c @@ -0,0 +1,35 @@ +/* example1.c - first example in the programming guide + * + * This file is Copyright (c) 2010 by the GPSD project + * BSD terms apply: see the file COPYING in the distribution root for details. + */ + +#include +#include +#include + +#include "microjson.h" + +static const char *simple = "{\"flag1\":true,\"flag2\":false,\"count\":42}"; + +static bool flag1, flag2; +static int count; + +static const struct json_attr_t json_attrs[] = { + {"count", t_integer, .addr.integer = &count}, + {"flag1", t_boolean, .addr.boolean = &flag1,}, + {"flag2", t_boolean, .addr.boolean = &flag2,}, + {NULL}, +}; + +int main(int argc, char *argv[]) +{ + int status = 0; + + puts(simple); + json_read_object(simple, json_attrs, NULL); + printf("count = %d, flag1 = %d, flag2 = %d\n", + count, flag1, flag2); +} + +/* end */ diff --git a/microjson.asc b/microjson.asc new file mode 100644 index 0000000..89913f4 --- /dev/null +++ b/microjson.asc @@ -0,0 +1,115 @@ += Building static JSON parsers with microjson = +Eric S. Raymond + +== Overview == + +microjson is a tiny parser for a large subset of JSON (JavaScript Object +Notation) with the distinguishing feature that it uses entirely +fixed-extent memory, no malloc(). It is thus very suitable for use in +memory-constrained environments such as embedded systems; also for +long-running service daemons that must provably not leak memory. + +microjson is extremely well-tested code. This is essentially the same +parser used in GPSD and its client libraries, which have hundreds of +millions of deployments underneath Google Maps in Android phones. + +microjson parses JSON from string input and unpacks the content +directly into static storage declared by the calling program. +You give it a set of template structures describing the expected shape +of the incoming JSON, and it will error out if that shape is not +matched. When the parse succeeds, attribute values will be extracted +into static locations specified in the template structures. + +== An Example == + +Here is nearly the simplest possible example: + +--------------------------------------------------------------------- +#include +#include +#include + +#include "microjson.h" + +static const char *simple = "{\"flag1\":true,\"flag2\":false,\"count\":42}"; + +static bool flag1, flag2; +static int count; + +static const struct json_attr_t json_attrs[] = { + {"count", t_integer, .addr.integer = &count}, + {"flag1", t_boolean, .addr.boolean = &flag1,}, + {"flag2", t_boolean, .addr.boolean = &flag2,}, + {NULL}, +}; + +int main(int argc, char *argv[]) +{ + int status = 0; + + puts(simple); + json_read_object(simple, json_attrs, NULL); + printf("count = %d, flag1 = %d, flag2 = %d\n", + count, flag1, flag2); +} + +/* end */ +--------------------------------------------------------------------- + +The +json_read_object()+ call unpacks the values in the example JSON object into +three static variables. In many uses the target locations would +instead be storage in some static structure instance. + +In this example, the +json_attrs+ structure array associates each +possible member name with a type and a target address. The function ++json_read_object()+ treats this array of constants as parsing +instructions. + +The parser recognizes a wider range of types than this, and the +template structures can specify defaults when an expected JSON +attribute is omitted. Most of the rest is details. + +== Theory of Operation == + +The "shape" of a JSON object in the type signature of its +attributes (and attribute values, and so on recursively down through +all nestings of objects and arrays). This parser is indifferent to +the order of attributes at any level, but you have to tell it in +advance what the type of each attribute value will be and where the +parsed value will be stored. The template structures may supply +default values to be used when an expected attribute is omitted. + +The preceding paragraph told one fib. A single attribute may actually +have a span of multiple specifications with different syntactically +distinguishable types (e.g. string vs. real vs. integer vs. boolean, +but not signed integer vs. unsigned integer). The parser will match +the right spec against the actual data. + +The dialect this parses has some limitations. First, it cannot +recognize the JSON "null" value. Secondly, arrays may only have +objects or strings - not reals or integers or floats - as elements +(this limitation could be easily removed if required). Third, all +elements of an array must be of the same type. + +There are separata entry points for beginning a parse of either JSON +object or a JSON array. JSON "float" quantities are actually stored as +doubles. + +This parser processes object arrays in one of two different ways, +defending on whether the array subtype is declared as object or +structobject. + + Object arrays take one base address per object subfield, and are +mapped into parallel C arrays (one per subfield). Strings are not +supported in this kind of array, as they don't have a "natural" size +to use as an offset multiplier. + + Structobjects arrays are a way to parse a list of objects to a set +of modifications to a corresponding array of C structs. The trick is +that the array object initialization has to specify both the C struct +array's base address and the stride length (the size of the C struct). +If you initialize the offset fields with the correct offsetof calls, +everything will work. Strings are supported but all string storage +has to be inline in the struct. + + diff --git a/microjson.c b/microjson.c index de51418..8687cda 100644 --- a/microjson.c +++ b/microjson.c @@ -54,7 +54,7 @@ everything will work. Strings are supported but all string storage has to be inline in the struct. PERMISSIONS - This file is Copyright (c) 2014 by the Eric S. Raymond + This file is Copyright (c) 2014 by Eric S. Raymond BSD terms apply: see the file COPYING in the distribution root for details. ***************************************************************************/ -- 2.43.0