json.c: Path Autovivifying JSON C Library

description

json.c is a JSON C library that supports path autovivification and conversion. Autovivified and converted paths greatly simplify manipulation of JSON trees, allowing one to discard most bothersome boilerplate code.

Because "JSON schema" is something of an oxymoron, the library makes the presumption that you mean what you say; that the schema is whatever the code does. If you attempt to write through a non-existent or type-incompatible path, the library fixes the tree to accomodate the request, rather than punt back to the caller. Likewise, a read through a non-existent or type-incompatible path returns a sane, default value—specifically 0 or an empty string.

In addition, a stack interface allows changing the current root. This makes descending up and down the tree more convenient, and eases abstraction as code which reads or writes subtrees need not be concerned with the path to that particular subtree.

Both the parser and composer are restartable and operate over a series of caller provided input and output buffers. This is intended to ease integration with networked I/O environments, particularly non-blocking environments. JSON data can be both parsed and composed byte-by-byte without any intermediate, internal buffers. No callback schemes are used as half measures, so the user code needn't arbitrarily split blocks of code between different halves of a parse or compose operation.

json.c is implemented in a single source file, along with a companion header which defines the API. The only dependency is llrb.h.

todo

Add json_splice—routine which performs the same tree splicing operation as the utility in examples/splice.c.

Handle sparse arrays, such as assiging index 99 of an array with only 10 elements.

Return to printf-style path format specifications, so GCC and clang can warn about type mismatches.

Write regression suite.

Investigate C++ wrapper using subscript and member access operator overloading.

Implement string interning for object keys.

news

2013-09-27

Slightly refactored path expansion and added missing va_end invocations reported by Coverity.

Added volatile qualifiers to variables in command-line utility main routine used after returning from an exception.

2013-09-26

Replaced _Bool with bool from <stdbool.h> in json.h and added extern "C" block to ease use from C++. Added splice++, a C++ build of examples/splice.c.

Refactored some bits in json.c to appease Solaris compiler.

2013-09-25

Made internal tree routines statically scoped.

Fixed some bugs with and finished the iterator API.

Added type introspection.

Added example of splicing trees together in examples/splice.c, which uses iteration API and type introspection.

Improved header documentation significantly.

Deprecated json_setlstring and json_v_setlstring in favor of json_setbuffer and json_v_setbuffer; and json_flush in favor of json_rewind.

Fixed flag handling code to properly obey flags. Added JSON_F_NOAUTOVIV and JSON_F_NOCONVERT, and make sure path API handles non-autovivified path traversals correctly.

Added JSON_F_PARTIAL, which tells the printing routines to print just the subtree at the current root, instead of unwinding the path stack and printing the entire document.

Fixed some issues with the path traversal code. It's slightly more strict now. Add JSON_EBADPATH and JSON_EBIGPATH to signal path parsing problems instead of overloading JSON_ESYNTAX.

Fixed bug where deleting the current root node left a dangling pointer in the path stack. Also handle case where evil application explicitly deletes a node down in the path stack.

Improved usage message of command-line utility, and added hooks to some new features.

2013-04-26

Applied patch from Sam Roberts which fixes json_parse() chunk-wise parsing. Previously the state was never properly finalized to allow the tree to be queried.

2013-02-18

Fixed the boundary between three-byte encoded UTF-8 and four-byte encoded. Report and patch from Steve Cai, Hulu.

Added iteration interface to public API.

2012-10-31

Added json_printstring() and json_ifthrow().

Documentation has been added to json.h describing usage for the parsing, composing, and error handling APIs.

Fixed \uNNNN decoding and improved Unicode handling in general.

2012-05-23

Tagged release rel-20120523.

json_setstring() contained a flawed strlen NULL check. Some consider silently treating NULL strings as empty strings poor behavior, so I'm not sure if this release constitutes a bug fix or a feature removal. I often consider it poor behavior, too, but the objective of json.c is to allow sloppy programming because JSON apps are generally slopppy regarding type consistency.

Updated json_loadstring() to also treat NULL as an empty string.

2012-05-12

Tagged release rel-20120512.

Fix comparison bug in error code range check macro, JSON_ERROR.

2012-05-07

Tagged release rel-20120507.

Printing bug fixed by 0-initializing the printer state on JSON object construction.

Added json_setlstring() and json_setstring() interfaces. This change was mistakenly left out of the original release and unfortunately breaks the API.

Some small internal refactoring to address macro conflicts with another project.

2012-03-24

Published.

usage

The API is broken up into three main sections. The grouping of declarations in the header reflects this, and makes for relatively easy perusal.

The core API consists of routines to construct a JSON context object, and to parse and compose JSON data.

The second set consists of routines to manipulate JSON value objects directly, although this is mostly used internally.

The third consists of routines which access and manipulate the JSON tree through a path expression. Objects are accessed with a dot ("."), arrays indexed by square brackets ("[", "]"). String interpolation is accomplished using the special character "$" and index interpolation using "#", along with their respective arguments (char pointer or int) in the passed variable argument list. The syntax uses the standard backslash escaping protocol.

The core API returns errors directly, while most of the remainder signal errors using longjmp(). The macro pair json_enter()/json_leave() are analogous to try/finally. However, thrown errors need not be caught; aren't thrown unless an exception context is provided; consistency is maintained if errors are ignored; and a sane and safe value always returned when reading a value. json.c error codes are negative numbers in the range JSON_EBASE to JSON_ELAST, and communicated along with system error codes through a single int value. POSIX guarantees system codes to be positive.

A small regression utility can be built from the source. Defining JSON_MAIN will expose the main() definition, and until documentation can be written usage can be gleaned from the implementation of the utility commands. The utility requires libffi in order to dynamically construct calls with interpolating path expressions, useful for exercising path evaluation. However, the include path for the libffi header is hit-and-miss. And a bug report has been filed with Apple because clang chokes on their broken ffi.h header.

examples

/* Generate the first example JSON snippet from RFC 4627:
 *
 * {
 * 	"Image": {
 * 		"Width":  800,
 * 		"Height": 600,
 * 		"Title":  "View from 15th Floor",
 * 		"Thumbnail": {
 * 			"Url":    "http://www.example.com/image/481989943",
 * 			"Height": 125,
 * 			"Width":  "100"
 * 		},
 * 		"IDs": [116, 943, 234, 38793]
 * 	}
 * }
 *
 * This example is more verbose than necessary. It's intended to exhibit the
 * behavior of autovivification, relative paths, and path interpolation.
 */
#include "json.h"

int main(void) {
	struct json *J;
	int error;

	J = json_open(JSON_F_NONE, &error);

	json_push(J, ".Image.Thumbnail");
	/* automatically instantiates . as an object, .Image as an object,
	 * and .Image.Thumbnail as a null value. .Image.Thumbnail is now the
	 * root node for path expressions.
	 */

	json_setstring(J, "http://www.example.com/image/481989943", "Url");
	/* automatically converts .Image.Thumbnail to an object and
	 * instantiates .Image.Thumbnail.Url to a string
	 */

	json_setnumber(J, 125, ".Height");
	json_setstring(J, "100", ".Width");

	json_pop(J);
	/* Our root node for path expressions is again the document root */

	json_setnumber(J, 800, ".Image.Width");
	json_setnumber(J, 600, ".Image.Height");

	json_setstring(J, "View from 15th Floor", ".Image.$", "Title");
	/* $ interpolates a string into the path expression */

	json_setnumber(J, 116, ".IDs[0]");
	/* .IDs is instantiated as an array and the number 116 set to the
	 * 0th index
	 */

	json_setnumber(J, 943, ".IDs[#]", json_count(J, ".IDs"));
	/* As an array index, # is taken as the index value. json_count
	 * returns the array size of .IDs as an int, which should be 1.
	 *
	 * (In an object key identifier, # interpolates an integer into the
	 * string key.)
	 */

	json_push(J, ".IDs[#]", json_count(J, ".IDs"));
	json_setnumber(J, 234, ".");
	json_pop(J);

	json_setnumber(J, 38793, ".IDs[3]");

	json_printfile(J, stdout, JSON_F_PRETTY);
	/* The JSON_F_PRETTY flag instructs the composer to print one value
	 * per line, and to indent each line with tabs according to its
	 * nested level
	 */

	json_close(J);

	return 0;
}
		

license

Copyright (c) 2012, 2013 William Ahern

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

source

git clone http://25thandClement.com/~william/projects/libjson.git

other projects

airctl | bsdauth | cnippets | libmime | libarena | libevnet | authldap | streamlocal | libnostd | zoned | dns.c | delegate.c | llrb.h | lpegk | json.c | cqueues | siphash.h | hexdump.c | timeout.c | luapath | luaossl | lunix | phf | runlua | AnonNet