From 2a2e37e5a103f477786a3c7f9ec6d3899d492c20 Mon Sep 17 00:00:00 2001 From: "Eric S. Raymond" Date: Tue, 23 Sep 2014 11:26:08 -0400 Subject: [PATCH] There are now three worked examples. --- .gitignore | 2 +- Makefile | 4 +- example1.c | 2 +- example2.c | 50 +++++++++++++++++ example3.c | 68 +++++++++++++++++++++++ microjson.asc | 148 ++++++++++++++++++++++++++++++++++++++++++++------ microjson.h | 2 +- 7 files changed, 255 insertions(+), 21 deletions(-) create mode 100644 example2.c create mode 100644 example3.c diff --git a/.gitignore b/.gitignore index b9b3be6..902731f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,3 @@ *.o test_microjson -example1 +example[123] diff --git a/Makefile b/Makefile index 4f4e62b..9de50ef 100644 --- a/Makefile +++ b/Makefile @@ -15,8 +15,10 @@ test_microjson: test_microjson.o microjson.o check: test_microjson test_microjson -# Worked example +# Worked examples example1: example1.c microjson.c +example2: example2.c microjson.c +example3: example3.c microjson.c clean: rm -f microjson.o test_microjson.o test_microjson example1 diff --git a/example1.c b/example1.c index 96aa855..81d370a 100644 --- a/example1.c +++ b/example1.c @@ -1,6 +1,6 @@ /* example1.c - first example in the programming guide * - * This file is Copyright (c) 2010 by the GPSD project + * This file is Copyright (c) 2014 by Eric S. Raymond * BSD terms apply: see the file COPYING in the distribution root for details. */ diff --git a/example2.c b/example2.c new file mode 100644 index 0000000..8780619 --- /dev/null +++ b/example2.c @@ -0,0 +1,50 @@ +/* example2.c - second example in the programming guide + * + * This file is Copyright (c) 2014 by Eric S. Raymond + * BSD terms apply: see the file COPYING in the distribution root for details. + */ +#include +#include + +#include "microjson.h" + +#define MAXCHANNELS 72 + +static bool usedflags[MAXCHANNELS]; +static int PRN[MAXCHANNELS]; +static int elevation[MAXCHANNELS]; +static int azimuth[MAXCHANNELS]; +static int visible; + +const struct json_attr_t sat_attrs[] = { + {"PRN", t_integer, .addr.integer = PRN}, + {"el", t_integer, .addr.integer = elevation}, + {"az", t_integer, .addr.integer = azimuth}, + {"used", t_boolean, .addr.boolean = usedflags}, + {NULL}, +}; + +const struct json_attr_t json_attrs_sky[] = { + {"class", t_check, .dflt.check = "SKY"}, + {"satellites", t_array, .addr.array.element_type = t_object, + .addr.array.arr.objects.subtype=sat_attrs, + .addr.array.maxlen = MAXCHANNELS, + .addr.array.count = &visible}, + {NULL}, + }; + +int main(int argc, char *argv[]) +{ + int i, status = 0; + + status = json_read_object(argv[1], json_attrs_sky, NULL); + printf("%d satellites:\n", visible); + for (i = 0; i < visible; i++) + printf("PRN = %d, elevation = %d, azimuth = %d\n", + PRN[i], elevation[i], azimuth[i]); + + if (status != 0) + puts(json_error_string(status)); +} + +/* end */ diff --git a/example3.c b/example3.c new file mode 100644 index 0000000..9b4c8e1 --- /dev/null +++ b/example3.c @@ -0,0 +1,68 @@ +/* example2.c - first example in the programming guide + * + * This file is Copyright (c) 2014 by Eric S. Raymond + * BSD terms apply: see the file COPYING in the distribution root for details. + */ +#include +#include +#include +#include +#include + +#include "microjson.h" + +#define MAXUSERDEVS 4 + +struct devconfig_t { + char path[PATH_MAX]; + double activated; +}; + +struct devlist_t { + int ndevices; + struct devconfig_t list[MAXUSERDEVS]; +}; + +static struct devlist_t devicelist; + +static int json_devicelist_read(const char *buf) +{ + const struct json_attr_t json_attrs_subdevice[] = { + {"path", t_string, STRUCTOBJECT(struct devconfig_t, path), + .len = sizeof(devicelist.list[0].path)}, + {"activated", t_time, STRUCTOBJECT(struct devconfig_t, activated)}, + {"activated", t_real, STRUCTOBJECT(struct devconfig_t, activated)}, + {NULL}, + }; + const struct json_attr_t json_attrs_devices[] = { + {"class", t_check,.dflt.check = "DEVICES"}, + {"devices", t_array, STRUCTARRAY(devicelist.list, + json_attrs_subdevice, + &devicelist.ndevices)}, + {NULL}, + }; + int status; + + memset(&devicelist, '\0', sizeof(devicelist)); + status = json_read_object(buf, json_attrs_devices, NULL); + if (status != 0) { + return status; + } + return 0; +} + +int main(int argc, char *argv[]) +{ + int i, status = 0; + + status = json_devicelist_read(argv[1]); + printf("%d devices:\n", devicelist.ndevices); + for (i = 0; i < devicelist.ndevices; i++) + printf("%s @ %f\n", + devicelist.list[i].path, devicelist.list[i].activated); + + if (status != 0) + puts(json_error_string(status)); +} + +/* end */ diff --git a/microjson.asc b/microjson.asc index c97053a..78d543f 100644 --- a/microjson.asc +++ b/microjson.asc @@ -20,11 +20,27 @@ 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. +== How To Use This Document == + +This is a fast tutorial for working programmers. It teaches by +examples; if you read the code carefully it will tell you +more than the accompanying test. Just read it in sequence, trying not +to skip anything. + +All the examples are shipped in the microjson distribution. They are +not synthetic toys, but stripped-down versions of working code from +GPS. Copy them freely. + == An Example == Here is nearly the simplest possible example: +.Example 1 --------------------------------------------------------------------- +/* + * Parse JSON shaped like '{"flag1":true,"flag2":false,"count":42}' + */ + #include #include #include @@ -143,7 +159,7 @@ t_array: Value of this attribute is expected to be a homogenous array. Another field of the structure specifies the array's element type, which can be any simple type or t_object (meaning a JSON subobject). -If the array has simple elements, two three additional things must be +If the array has simple elements, three additional things must be specified: the base address of the array's storage, the maximum number of elements it can have, and an integer address where the parser will place a count of elements filled in. @@ -154,12 +170,20 @@ for booleans, and NULL for strings. The array element type may be +t_object+, as in the +satellites+ field in this example: +.Example 2 ------------------------------------------------------------------------ -bool usedflags[MAXCHANNELS]; -int PRN[MAXCHANNELS]; -int elevation[MAXCHANNELS]; -int azimuth[MAXCHANNELS]; -int visible; +#include +#include + +#include "microjson.h" + +#define MAXCHANNELS 72 + +static bool usedflags[MAXCHANNELS]; +static int PRN[MAXCHANNELS]; +static int elevation[MAXCHANNELS]; +static int azimuth[MAXCHANNELS]; +static int visible; const struct json_attr_t sat_attrs[] = { {"PRN", t_integer, .addr.integer = PRN}, @@ -177,20 +201,31 @@ const struct json_attr_t json_attrs_sky[] = { .addr.array.count = &visible}, {NULL}, }; + +int main(int argc, char *argv[]) +{ + int i, status = 0; + + status = json_read_object(argv[1], json_attrs_sky, NULL); + printf("%d satellites:\n", visible); + for (i = 0; i < visible; i++) + printf("PRN = %d, elevation = %d, azimuth = %d\n", + PRN[i], elevation[i], azimuth[i]); + + if (status != 0) + puts(json_error_string(status)); +} ------------------------------------------------------------------------ -An example of the shape of JSON this would unpack is: +Here's an example invocation (string literal folded for readability): -------------------------------------------------------- -{"class":"SKY","satellites":[ - {"PRN":10,"el":45,"az":196,"used":true}, - {"PRN":29,"el":67,"az":310,"used":true}, - {"PRN":28,"el":59,"az":108,"used":true}, - {"PRN":26,"el":51,"az":304,"used":true}, - {"PRN":8, "el":44,"az":58, "used":true}, - {"PRN":27,"el":16,"az":66, "used":true}, - {"PRN":21,"el":10,"az":301,"used":false} -]} +$ example2 '{"class":"SKY","satellites": + [{"PRN":10,"el":45,"az":196,"used":true}, + {"PRN":29,"el":67,"az":310,"used":true}]}' +2 satellites: +PRN = 10, elevation = 45, azimuth = 196 +PRN = 29, elevation = 67, azimuth = 310 -------------------------------------------------------- In this case, the parser needs to be told where to find a template @@ -201,10 +236,89 @@ sub-objects to parse or would exceed the +maxlen+ count of elements. ==== Structure arrays ==== -t_structobject: this also parses +There's a different way to parse arrays that can unpack an +array of JSON objects directly into an array of C structs. +.Example 3: +-------------------------------------------------------- +#include +#include +#include +#include +#include +#include +#include + +#include "microjson.h" +#define MAXUSERDEVS 4 + +struct devconfig_t { + char path[PATH_MAX]; + double activated; +}; + +struct devlist_t { + int ndevices; + struct devconfig_t list[MAXUSERDEVS]; +}; + +static struct devlist_t devicelist; + +static int json_devicelist_read(const char *buf) +{ + const struct json_attr_t json_attrs_subdevice[] = { + {"path", t_string, STRUCTOBJECT(struct devconfig_t, path), + .len = sizeof(devicelist.list[0].path)}, + {"activated", t_time, STRUCTOBJECT(struct devconfig_t, activated)}, + {"activated", t_real, STRUCTOBJECT(struct devconfig_t, activated)}, + {NULL}, + }; + const struct json_attr_t json_attrs_devices[] = { + {"class", t_check,.dflt.check = "DEVICES"}, + {"devices", t_array, STRUCTARRAY(devicelist.list, + json_attrs_subdevice, + &devicelist.ndevices)}, + {NULL}, + }; + int status; + + memset(&devicelist, '\0', sizeof(devicelist)); + status = json_read_object(buf, json_attrs_devices, NULL); + if (status != 0) { + return status; + } + return 0; +} + +int main(int argc, char *argv[]) +{ + int i, status = 0; + + status = json_devicelist_read(argv[1]); + printf("%d devices:\n", devicelist.ndevices); + for (i = 0; i < devicelist.ndevices; i++) + printf("%s @ %f\n", + devicelist.list[i].path, devicelist.list[i].activated); + + if (status != 0) + puts(json_error_string(status)); +} +-------------------------------------------------------- + +Here is an example: + +-------------------------------------------------------- +$ example3 '{"devices":[{"path":"/dev/ttyUSB0", + "activated":"2014-09-23T10:32:20"}]}' +1 devices: +/dev/ttyUSB0 @ 1411468340.000000 +-------------------------------------------------------- +In this case, the STRUCTARRAY and STRUCTOBJECT macros are clues to +what is going on. STRUCTOBJECT is a thin wrapper around offsetof(); +STRUCTARRAY sets up the parser to walk through the array of +structures, filling each element as it goes. diff --git a/microjson.h b/microjson.h index 0629811..8323d95 100644 --- a/microjson.h +++ b/microjson.h @@ -134,6 +134,6 @@ void json_enable_debug(int, FILE *); .addr.array.arr.objects.base = (char*)a, \ .addr.array.arr.objects.stride = sizeof(a[0]), \ .addr.array.count = n, \ - .addr.array.maxlen = NITEMS(a) + .addr.array.maxlen = (int)(sizeof(a)/sizeof(a[0])) /* json.h ends here */ -- 2.43.0