From 556eabfcd586d414728b32b6ce0e2740ef5c2af6 Mon Sep 17 00:00:00 2001 From: Martin Mares Date: Wed, 8 Jul 2015 20:27:00 +0200 Subject: [PATCH] JSON: Basic documentation --- ucw-json/doc/index.txt | 4 +- ucw-json/doc/json.txt | 14 ++--- ucw-json/json.h | 129 ++++++++++++++++++++++++++++++++++++----- 3 files changed, 123 insertions(+), 24 deletions(-) diff --git a/ucw-json/doc/index.txt b/ucw-json/doc/index.txt index 0f1fc691..de708085 100644 --- a/ucw-json/doc/index.txt +++ b/ucw-json/doc/index.txt @@ -3,9 +3,11 @@ The UCW-JSON library This library provides a light-weight JSON parser and generator built atop <<../ucw/index:,LibUCW>>. +It follows the specification of JSON in RFC 7159. + Modules ------- -- <> +- <> Authors ------- diff --git a/ucw-json/doc/json.txt b/ucw-json/doc/json.txt index 3d88e035..a4e77ee2 100644 --- a/ucw-json/doc/json.txt +++ b/ucw-json/doc/json.txt @@ -1,14 +1,12 @@ -JSON Parser -=========== +JSON Parser and Generator +========================= ucw-json/json.h -------------- +--------------- -FIXME +To use the JSON library, you need to create a context (<>) +by calling <> first. -To parse a document, create a parser context (<>), -initialize it with <>, fill in requested parsing mode, pointers to hooks, and -other parameters. Then call <> or <> as you need. At the end, dispose -of the context by <> or recycle it by <>. +FIXME !!ucw-json/json.h diff --git a/ucw-json/json.h b/ucw-json/json.h index aa1d9f8a..aff1fccc 100644 --- a/ucw-json/json.h +++ b/ucw-json/json.h @@ -36,30 +36,65 @@ #endif /*** - * === FIXME + * === JSON library context + * + * FIXME: Document memory management ***/ +/** + * The context is represented a pointer to this structure. + * The fields marked with [*] are publicly accessible, the rest is private. + **/ struct json_context { + // Memory management struct mempool *pool; struct mempool_state init_state; + // Parser context struct fastbuf *in_fb; - uint in_line; - uint in_column; - bool in_eof; + uint in_line; // [*] Current line number + uint in_column; // [*] Current column number + bool in_eof; // End of file was encountered struct json_node *next_token; struct json_node *trivial_token; int next_char; + // Formatter context struct fastbuf *out_fb; uint out_indent; - uint format_options; // Public + uint format_options; // [*] Formatting options (a combination of JSON_FORMAT_xxx) }; +/** Creates a new JSON context. **/ struct json_context *json_new(void); + +/** Deletes a JSON context, deallocating all memory associated with it. **/ void json_delete(struct json_context *js); + +/** + * Recycles a JSON context. All state is reset, allocated objects are freed. + * This is equivalent to mp_delete() followed by mp_new(), but it is faster + * and the address of the context is preserved. + **/ void json_reset(struct json_context *js); +/*** + * === JSON values + * + * Each JSON value is represented by <>, + * which is either an elementary value (null, boolean, number, string), + * or a container (array, object) pointing to other values. + * + * A value can belong to multiple containers simultaneously, so in general, + * the relationships between values need not form a tree, but a directed + * acyclic graph. + * + * You are allowed to read contents of nodes directly, but construction + * and modification of nodes must be always performed using the appropriate + * library functions. + ***/ + +/** Node types **/ enum json_node_type { JSON_INVALID, JSON_NULL, @@ -68,7 +103,9 @@ enum json_node_type { JSON_STRING, JSON_ARRAY, JSON_OBJECT, - // These are not real nodes, but raw tokens + // These are not real nodes, but raw tokens. + // They are not present in the tree of values, but you may see them + // if you call json_next_token() and friends. JSON_BEGIN_ARRAY, JSON_END_ARRAY, JSON_BEGIN_OBJECT, @@ -78,30 +115,35 @@ enum json_node_type { JSON_EOF, }; +/** Each value is represented by a single node. **/ struct json_node { enum json_node_type type; - union { + union { // Data specific to individual value types bool boolean; double number; const char *string; - struct json_node **elements; // Growing array - struct json_pair *pairs; // Growing array + struct json_node **elements; // Arrays: Growing array of values + struct json_pair *pairs; // Objects: Growing array of pairs }; }; +/** Attributes of objects are stored as (key, value) pairs of this format. **/ struct json_pair { const char *key; struct json_node *value; // FIXME: Hash table }; +// Used internally struct json_node *json_new_node(struct json_context *js, enum json_node_type type); +/** Creates a new null value. **/ static inline struct json_node *json_new_null(struct json_context *js) { return json_new_node(js, JSON_NULL); } +/** Creates a new boolean value. **/ static inline struct json_node *json_new_bool(struct json_context *js, bool value) { struct json_node *n = json_new_node(js, JSON_BOOLEAN); @@ -109,6 +151,7 @@ static inline struct json_node *json_new_bool(struct json_context *js, bool valu return n; } +/** Creates a new numeric value. **/ static inline struct json_node *json_new_number(struct json_context *js, double value) { struct json_node *n = json_new_node(js, JSON_NUMBER); @@ -116,6 +159,7 @@ static inline struct json_node *json_new_number(struct json_context *js, double return n; } +/** Creates a new string value. The @value is kept only as a reference. **/ static inline struct json_node *json_new_string_ref(struct json_context *js, const char *value) { struct json_node *n = json_new_node(js, JSON_STRING); @@ -123,34 +167,89 @@ static inline struct json_node *json_new_string_ref(struct json_context *js, con return n; } +/** Creates a new string value, making a private copy of @value. **/ static inline struct json_node *json_new_string(struct json_context *js, const char *value) { return json_new_string_ref(js, mp_strdup(js->pool, value)); } +/** Creates a new array value with no elements. **/ struct json_node *json_new_array(struct json_context *js); + +/** Appends a new element to the given array. **/ void json_array_append(struct json_node *array, struct json_node *elt); +/** Creates a new object value with no attributes. **/ struct json_node *json_new_object(struct json_context *js); -// FIXME: key must not be freed + +/** + * Adds a new (@key, @value) pair to the given object. If @key is already + * present, the pair is replaced. If @value is NULL, no new pair is created + * and a pre-existing pair is deleted. + * + * The @key is referenced by the object, you must not free it during + * the lifetime of the JSON context. + * + * FIXME: Add json_copy_key(). + **/ void json_object_set(struct json_node *n, const char *key, struct json_node *value); + +/** Returns the value associated with @key, or NULL if no such value exists. **/ struct json_node *json_object_get(struct json_node *n, const char *key); +/*** + * === Parser + * + * The simplest way to parse a complete JSON file is to call json_parse(), + * which returns a value tree representing the contents of the file. + * + * Alternatively, you can read the input token by token: call json_set_input() + * and then repeat json_next_token(). Note that even in this mode, the tokens + * are kept in memory. (If you need to parse huge JSON files, please ask the + * maintainer of this library to improve memory allocation.) + ***/ + +/** Parses a JSON file from the given fastbuf stream. **/ +struct json_node *json_parse(struct json_context *js, struct fastbuf *fb); + +/** Selects the given fastbuf stream as parser input. **/ void json_set_input(struct json_context *js, struct fastbuf *in); -struct json_node *json_peek_token(struct json_context *js); + +/** Reads the next token from the input. **/ struct json_node *json_next_token(struct json_context *js); +/** Reads the next token, but keeps it in the input. **/ +struct json_node *json_peek_token(struct json_context *js); + +/** Reads the next JSON value, including nested values. **/ struct json_node *json_next_value(struct json_context *js); -struct json_node *json_parse(struct json_context *js, struct fastbuf *fb); +/*** + * === Writer + * + * JSON files can be produced by simply calling json_write(). + * + * If you want to generate the output on the fly (for example if it is huge), + * call json_set_output() and then iterate json_write_value(). + * + * By default, we produce a single-line compact representation, + * but you can choose differently by setting the appropriate + * `format_options` in the `json_context`. + ***/ +/** Writes a JSON file to the given fastbuf stream, containing the JSON value @n. **/ +void json_write(struct json_context *js, struct fastbuf *fb, struct json_node *n); + +/** Selects the given fastbuf stream as output. **/ void json_set_output(struct json_context *js, struct fastbuf *fb); + +/** Writes a single JSON value to the output stream. **/ void json_write_value(struct json_context *js, struct json_node *n); -void json_write(struct json_context *js, struct fastbuf *fb, struct json_node *n); +/** Formatting options. The `format_options` field in the context is a bitwise OR of these flags. **/ enum json_format_option { - JSON_FORMAT_ESCAPE_NONASCII = 1, - JSON_FORMAT_INDENT = 2, + JSON_FORMAT_ESCAPE_NONASCII = 1, // Produce pure ASCII output by escaping all Unicode characters in strings + JSON_FORMAT_INDENT = 2, // Produce pretty indented output }; #endif -- 2.39.2