1. Add doxygen markup for documentation.
authorscottmk <scottmk@9efc2488-bf62-4759-914b-345cdb29e865>
Fri, 25 Sep 2009 14:35:18 +0000 (14:35 +0000)
committerscottmk <scottmk@9efc2488-bf62-4759-914b-345cdb29e865>
Fri, 25 Sep 2009 14:35:18 +0000 (14:35 +0000)
2. In jsonNewObjectType(): explicitly initialize a JSON_BOOL to false, instead
of implicitly relying on the expectation that a NULL pointer is represented
by all-bits-zero.

3. In jsonObjectExtractIndex(): set the parent pointer to NULL in the
extracted jsonObject.

M    include/opensrf/osrf_json.h
M    src/libopensrf/osrf_json_object.c

git-svn-id: svn://svn.open-ils.org/OpenSRF/trunk@1794 9efc2488-bf62-4759-914b-345cdb29e865

include/opensrf/osrf_json.h
src/libopensrf/osrf_json_object.c

index 4dd8ead..b7fb8fc 100644 (file)
@@ -13,6 +13,38 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.
 */
 
+/**
+       @file osrf_json.h
+       @brief Header for parsing JSON structures and representing them in memory.
+
+       JSON is a format for representing hierarchical data structures as text.
+
+       A JSON string may be a quoted string, a numeric literal, or any of the keywords true, false
+       and null.
+
+       A JSON string may also be an array, i.e. a series of values, separated by commas and enclosed
+       in square brackets.  For example: [ "Adams", 42, null, true ]
+
+       A JSON string may also be an object, i.e. a series of name/value pairs, separated by commas
+       and enclosed by curly braces.  Each name/value pair is a quoted string, followed by a colon,
+       followed by a value.  For example: { "Author":"Adams", "answer":42, "question":null,
+       "profound": true }
+
+       The values in a JSON array or object may themselves be arrays or objects, nested to any
+       depth, in a hierarchical structure of arbitrary complexity.  For more information about
+       JSON, see http://json.org/.
+
+       Like a JSON string, a jsonObject can take several different forms.  It can hold a string, a
+       number, a boolean, or a null.  It can also hold an array, implemented internally by an
+       osrfList (see osrf_list.h).  It can also hold a series of name/value pairs, implemented
+       internally by an osrfHash (see osrf_hash.h).
+
+       A jsonObject can also tag its contents with a class name, typically referring to a
+       database table or view.  Such an object can be translated into a JSON string where the
+       class is encoded as the value of a name/value pair, with the original jsonObject encoded
+       as the value of a second name/value pair.
+*/
+
 #ifndef JSON_H
 #define JSON_H
 
@@ -25,6 +57,14 @@ extern "C" {
 #endif
 
 /* parser states */
+/**
+       @name Parser states
+       @brief Used internally by a JSON parser.
+
+       A jsonParserContext stores these values in order to remember where the parser is in the
+       parsing.
+*/
+/*@{*/
 #define JSON_STATE_IN_OBJECT   0x1
 #define JSON_STATE_IN_ARRAY            0x2
 #define JSON_STATE_IN_STRING   0x4
@@ -40,53 +80,107 @@ extern "C" {
 #define JSON_STATE_START_COMMEN        0x1000
 #define JSON_STATE_IN_COMMENT  0x2000
 #define JSON_STATE_END_COMMENT 0x4000
+/*@}*/
 
+/**
+       @name Parser state operations
+       @ Macros to manipulate the parser state in a jsonParserContext.
+*/
+/*@{*/
+/** Set a state. */
+#define JSON_STATE_SET(ctx,s) ctx->state |= s; 
+/** Unset a state. */
+#define JSON_STATE_REMOVE(ctx,s) ctx->state &= ~s;
+/** Check if a state is set. */
+#define JSON_STATE_CHECK(ctx,s) (ctx->state & s) ? 1 : 0
+/** Pop a state off the stack. */
+#define JSON_STATE_POP(ctx) osrfListPop( ctx->stateStack );
+/** Push a state on the stack. */
+#define JSON_STATE_PUSH(ctx, state) osrfListPush( ctx->stateStack,(void*) state );
+/** Check which container type we're currently in. */
+#define JSON_STATE_PEEK(ctx) osrfListGetIndex(ctx->stateStack, ctx->stateStack->size -1)
+/** Compare stack values. */
+#define JSON_STATE_CHECK_STACK(ctx, s) (JSON_STATE_PEEK(ctx) == (void*) s ) ? 1 : 0
+/** Check if a parser state is set. */
+#define JSON_PARSE_FLAG_CHECK(ctx, f) (ctx->flags & f) ? 1 : 0
+/*@}*/
 
-/* object and array (container) states are pushed onto a stack so we
- * can keep track of the object nest.  All other states are
- * simply stored in the state field of the parser */
-#define JSON_STATE_SET(ctx,s) ctx->state |= s; /* set a state */
-#define JSON_STATE_REMOVE(ctx,s) ctx->state &= ~s; /* unset a state */
-#define JSON_STATE_CHECK(ctx,s) (ctx->state & s) ? 1 : 0 /* check if a state is set */
-#define JSON_STATE_POP(ctx) osrfListPop( ctx->stateStack ); /* remove a state from the stack */
-#define JSON_STATE_PUSH(ctx, state) osrfListPush( ctx->stateStack,(void*) state );/* push a state on the stack */
-#define JSON_STATE_PEEK(ctx) osrfListGetIndex(ctx->stateStack, ctx->stateStack->size -1) /* check which container type we're currently in */
-#define JSON_STATE_CHECK_STACK(ctx, s) (JSON_STATE_PEEK(ctx) == (void*) s ) ? 1 : 0  /* compare stack values */
+/**
+       @name JSON types
+       @brief Macros defining types of jsonObject.
 
-/* JSON types */
+       A jsonObject includes a @em type member with one of these values, identifying the type of
+       jsonObject.  Client code should treat the @em type member as read-only.
+*/
+/*@{*/
 #define JSON_HASH      0
 #define JSON_ARRAY     1
 #define JSON_STRING    2
 #define JSON_NUMBER    3
-#define JSON_NULL      4       
+#define JSON_NULL      4
 #define JSON_BOOL      5
+/*@}*/
 
+/**
+       This macro is used only by a JSON parser.  It probably has no business being published
+       in a header.
+*/
 #define JSON_PARSE_LAST_CHUNK 0x1 /* this is the last part of the string we're parsing */
 
-#define JSON_PARSE_FLAG_CHECK(ctx, f) (ctx->flags & f) ? 1 : 0 /* check if a parser state is set */
+/**
+       @name JSON extensions
+
+       These two macros define tags used for encoding class information.  @em JSON_CLASS_KEY
+       labels a class name, and @em JSON_DATA_KEY labels an associated value.
 
+       Because each of these macros is subject to an ifndef clause, client code may override
+       the default choice of labels by defining alternatives before including this header.
+       At this writing, this potential for customization is unused.
+*/
+/*@{*/
 #ifndef JSON_CLASS_KEY
 #define JSON_CLASS_KEY "__c"
 #endif
 #ifndef JSON_DATA_KEY
 #define JSON_DATA_KEY "__p"
 #endif
+/*@}*/
 
+/**
+       @brief Stores the current state of a JSON parser.
+
+       One form of JSON parser operates as a finite state machine.  It stores the various
+       JSON_STATE_* values in order to keep track of what it's doing.  It also maintains a
+       stack of previous states in order to keep track of nesting.
 
+       The internals of this struct are published in the header in order to provide the client
+       with a window into the operations of the parser.  By installing its own callback functions,
+       and possibly by tinkering with the insides of the jsonParserContext, the client code can
+       customize the behavior of the parser.
+
+       In practice only the default callbacks are ever installed, at this writing.  The potential
+       for customized parsing is unused.
+*/
 struct jsonParserContextStruct {
-       int state;                                              /* what are we currently parsing */
-       const char* chunk;                              /* the chunk we're currently parsing */
-       int index;                                              /* where we are in parsing the current chunk */
-       int chunksize;                                  /* the size of the current chunk */
-       int flags;                                              /* parser flags */
-       osrfList* stateStack;           /* represents the nest of object/array states */
-       growing_buffer* buffer;         /* used to hold JSON strings, number, true, false, and null sequences */
-       growing_buffer* utfbuf;         /* holds the current unicode characters */
-       void* userData;                         /* opaque user pointer.  we ignore this */
-       const struct jsonParserHandlerStruct* handler; /* the event handler struct */
+       int state;                  /**< What are we currently parsing. */
+       const char* chunk;          /**< The chunk we're currently parsing. */
+       int index;                  /**< Where we are in parsing the current chunk. */
+       int chunksize;              /**< Size of the current chunk. */
+       int flags;                  /**< Parser flags. */
+       osrfList* stateStack;       /**< The nest of object/array states. */
+       growing_buffer* buffer;     /**< Buffer for building strings, numbers, and keywords. */
+       growing_buffer* utfbuf;     /**< Holds the current unicode characters. */
+       void* userData;             /**< Opaque user pointer.  We ignore this. */
+       const struct jsonParserHandlerStruct* handler; /**< The event handler struct. */
 };
 typedef struct jsonParserContextStruct jsonParserContext;
 
+/**
+       @brief A collection of function pointers for customizing parser behavior.
+
+       The client code can install pointers to its own functions in this struct, in order to
+       customize the behavior of the parser at various points in the parsing.
+*/
 struct jsonParserHandlerStruct {
        void (*handleStartObject)       (void* userData);
        void (*handleObjectKey)         (void* userData, char* key);
@@ -94,234 +188,194 @@ struct jsonParserHandlerStruct {
        void (*handleStartArray)        (void* userData);
        void (*handleEndArray)          (void* userData);
        void (*handleNull)                      (void* userData);
-       void (*handleString)                    (void* userData, char* string);
+       void (*handleString)            (void* userData, char* string);
        void (*handleBool)                      (void* userData, int boolval);
        void (*handleNumber)            (void* userData, const char* numstr);
        void (*handleError)                     (void* userData, char* err, ...);
 };
 typedef struct jsonParserHandlerStruct jsonParserHandler;
 
+/**
+       @brief Representation of a JSON string in memory
+
+       Different types of jsonObject use different members of the @em value union.
+
+       Numbers are stored as character strings, using the same buffer that we use for
+       strings.  As a result, a JSON_NUMBER can store numbers that cannot be represented
+       by native C types.
+
+       (We used to store numbers as doubles.  We still have the @em n member lying around as
+       a relic of those times, but we don't use it.  We can't get rid of it yet, either.  Long
+       story.)
+*/
 struct _jsonObjectStruct {
-       unsigned long size;     /* number of sub-items */
-       char* classname;                /* optional class hint (not part of the JSON spec) */
-       int type;                               /* JSON type */
-       struct _jsonObjectStruct* parent;       /* who we're attached to */
-       union __jsonValue {     /* cargo */
-               osrfHash*       h;              /* object container */
-               osrfList*       l;              /* array container */
-               char*           s;              /* string */
-               int                     b;              /* bool */
-//             double  n;              /* number */
-               double  n;              /* number */
+       unsigned long size;     /**< Number of sub-items. */
+       char* classname;        /**< Optional class hint (not part of the JSON spec). */
+       int type;               /**< JSON type. */
+       struct _jsonObjectStruct* parent;   /**< Whom we're attached to. */
+       /** Union used for various types of cargo. */
+       union _jsonValue {
+               osrfHash*       h;      /**< Object container. */
+               osrfList*       l;      /**< Array container. */
+               char*           s;      /**< String or number. */
+               int             b;      /**< Bool. */
+               double  n;          /**< Number (no longer used). */
        } value;
 };
 typedef struct _jsonObjectStruct jsonObject;
 
+/**
+       @brief Iterator for traversing a jsonObject.
+
+       A jsonIterator traverses a jsonIterator only at a single level.  It does @em not descend
+       into lower levels to traverse them recursively.
+*/
 struct _jsonIteratorStruct {
-       jsonObject* obj; /* the object we're traversing */
-       osrfHashIterator* hashItr; /* the iterator for this hash */
-       const char* key; /* if this object is a hash, the current key */
-       unsigned long index; /* if this object is an array, the index */
+       jsonObject* obj;           /**< The object we're traversing. */
+       osrfHashIterator* hashItr; /**< The iterator for this hash. */
+       const char* key;           /**< If this object is a hash, the current key. */
+       unsigned long index;       /**< If this object is an array, the index. */
 };
 typedef struct _jsonIteratorStruct jsonIterator;
 
 
 
 /** 
- * Allocates a new parser context object
- * @param handler The event handler struct
- * @param userData Opaque user pointer which is available in callbacks
- * and ignored by the parser
- * @return An allocated parser context, NULL on error
- */
+       @brief Allocate a new parser context object.
+       @param handler Pointer to a collection of function pointers for callback functions.
+       @param userData Opaque user pointer which is available to callbacks; ignored by the parser.
+       @return An allocated parser context, or NULL on error.
+*/
 jsonParserContext* jsonNewParser( const jsonParserHandler* handler, void* userData);
 
 /**
- * Deallocates a parser context
- * @param ctx The context object
- */
+       @brief Free a jsonParserContext.
+       @param ctx Pointer to the jsonParserContext to be freed.
+*/
 void jsonParserFree( jsonParserContext* ctx );
 
 /**
- * Parse a chunk of data.
- * @param ctx The parser context
- * @param data The data to parse
- * @param datalen The size of the chunk to parser
- * @param flags Reserved
- */
+       @brief Parse a chunk of data.
+       @param ctx Pointer to the parser context.
+       @param data Pointer the data to parse.
+       @param datalen The size of the chunk to parse.
+       @param flags Reserved.
+*/
 int jsonParseChunk( jsonParserContext* ctx, const char* data, int datalen, int flags );
 
+/**
+       @name Parsing functions
+       
+       There are two sets of parsing functions, which are mostly plug-compatible with each other.
+       The older series:
 
+       - jsonParseString()
+       - jsonParseStringRaw()
+       - jsonParseStringFmt()
+
+       ...and a newer series:
+
+       - jsonParseString();
+       - jsonParseStringRaw();
+       - jsonParseStringFmt();
+
+       The first series is based on a finite state machine.  Its innards are accessible, in
+       theory, through the jsonParserContext structure and through callback functions.  In
+       practice this flexibility is unused at this writing.
+
+       The second series is based on recursive descent.  It doesn't use the jsonParserContext
+       structure, nor does it accept callback functions.  However it is faster than the first
+       parser.  In addition its syntax checking is much stricter -- it catches many kinds of
+       syntax errors that slip through the first parser.
+*/
+/*@{*/
 /**
- * Parses a JSON string;
- * @param str The string to parser
- * @return The resulting JSON object or NULL on error
- */
+       @brief Parse a JSON string;
+       @param str Pointer to the JSON string to parse.
+       @return The resulting JSON object, or NULL on error.
+*/
 jsonObject* jsonParseString( const char* str );
 jsonObject* jsonParseStringRaw( const char* str );
-
 jsonObject* jsonParseStringFmt( const char* str, ... );
 
 /**
- * Similar to jsonParseString(), jsonParseStringRaw() and
- * jsonParseStringFmt(), but with stricter and more robust
- * syntax validation
+       @brief Parse a JSON string;
+       @param s Pointer to the JSON string to parse.
+       @return The resulting JSON object, or NULL on error.
  */
 jsonObject* jsonParse( const char* s );
 jsonObject* jsonParseRaw( const char* s );
 jsonObject* jsonParseFmt( const char* str, ... );
-
+/*@}*/
 
 /**
- * Parses a JSON string;
- * @param str The string to parser
- * @return The resulting JSON object or NULL on error
+       @brief Parses a JSON string, using a customized error handler.
+       @param errorHandler A function pointer to an error-handling function.
+       @param str The string to parse.
+       @return The resulting JSON object, or NULL on error.
  */
 jsonObject* jsonParseStringHandleError( void (*errorHandler) (const char*), char* str, ... );
 
-
-
-/**
- * Creates a new json object
- * @param data The string data this object will hold if 
- * this object happens to be a JSON_STRING, NULL otherwise
- * @return The allocated json object.  Must be freed with 
- * jsonObjectFree()
- */
 jsonObject* jsonNewObject(const char* data);
+
 jsonObject* jsonNewObjectFmt(const char* data, ...);
 
-/**
- * Creates a new object of the given type
- */
 jsonObject* jsonNewObjectType(int type);
 
-/**
- * Creates a new number object from a double
- */
 jsonObject* jsonNewNumberObject( double num );
 
-/**
- * Creates a new number object from a numeric string
- */
 jsonObject* jsonNewNumberStringObject( const char* numstr );
 
-/**
- * Creates a new json bool
- */
 jsonObject* jsonNewBoolObject(int val);
 
-/**
- * Deallocates an object
- */
 void jsonObjectFree( jsonObject* o );
 
-/**
- * Returns all unused jsonObjects to the heap
- */
 void jsonObjectFreeUnused( void );
 
-/**
- * Forces the given object to become an array (if it isn't already one) 
- * and pushes the new object into the array
- */
 unsigned long jsonObjectPush(jsonObject* o, jsonObject* newo);
 
-/**
- * Forces the given object to become a hash (if it isn't already one)
- * and assigns the new object to the key of the hash
- */
 unsigned long jsonObjectSetKey(
                jsonObject* o, const char* key, jsonObject* newo);
 
-
-/**
+/*
  * Turns the object into a JSON string.  The string must be freed by the caller */
 char* jsonObjectToJSON( const jsonObject* obj );
 char* jsonObjectToJSONRaw( const jsonObject* obj );
 
-
-/**
- * Retrieves the object at the given key
- */
 jsonObject* jsonObjectGetKey( jsonObject* obj, const char* key );
-const jsonObject* jsonObjectGetKeyConst( const jsonObject* obj, const char* key );
-
-
-
 
+const jsonObject* jsonObjectGetKeyConst( const jsonObject* obj, const char* key );
 
-/** Allocates a new iterator 
-       @param obj The object over which to iterate.
-*/
 jsonIterator* jsonNewIterator(const jsonObject* obj);
 
+void jsonIteratorFree(jsonIterator* itr);
 
-/** 
-       De-allocates an iterator 
-       @param iter The iterator object to free
-*/
-void jsonIteratorFree(jsonIterator* iter);
-
-/** 
-       Returns the object_node currently pointed to by the iterator
-       and increments the pointer to the next node
-       @param iter The iterator in question.
- */
 jsonObject* jsonIteratorNext(jsonIterator* iter);
 
+int jsonIteratorHasNext(const jsonIterator* itr);
 
-/** 
-       @param iter The iterator.
-       @return True if there is another node after the current node.
- */
-int jsonIteratorHasNext(const jsonIterator* iter);
-
-
-/** 
-       Returns a pointer to the object at the given index.  This call is
-       only valid if the object has a type of JSON_ARRAY.
-       @param obj The object
-       @param index The position within the object
-       @return The object at the given index.
-*/
 jsonObject* jsonObjectGetIndex( const jsonObject* obj, unsigned long index );
 
-
-/* removes (and deallocates) the object at the given index (if one exists) and inserts 
- * the new one.  returns the size on success, -1 on error 
- * If obj is NULL, inserts a new object into the list with is_null set to true
- */
 unsigned long jsonObjectSetIndex(jsonObject* dest, unsigned long index, jsonObject* newObj);
 
-/* removes and deallocates the object at the given index, replacing
-   it with a NULL pointer
- */
 unsigned long jsonObjectRemoveIndex(jsonObject* dest, unsigned long index);
 
-/* removes (but does not deallocate) the object at the given index,
- * replacing it with a NULL pointer; returns a pointer to the object removed
- */
 jsonObject* jsonObjectExtractIndex(jsonObject* dest, unsigned long index);
 
-/* removes (and deallocates) the object with key 'key' if it exists */
 unsigned long jsonObjectRemoveKey( jsonObject* dest, const char* key);
 
-/* returns a pointer to the string data held by this object if this object
-       is a string.  Otherwise returns NULL*/
 char* jsonObjectGetString(const jsonObject*);
 
 double jsonObjectGetNumber( const jsonObject* obj );
 
-/* sets the string data */
 void jsonObjectSetString(jsonObject* dest, const char* string);
 
-/* sets the number value for the object */
 void jsonObjectSetNumber(jsonObject* dest, double num);
+
 int jsonObjectSetNumberString(jsonObject* dest, const char* string);
 
-/* sets the class hint for this object */
 void jsonObjectSetClass(jsonObject* dest, const char* classname );
+
 const char* jsonObjectGetClass(const jsonObject* dest);
 
 int jsonBoolIsTrue( const jsonObject* boolObj );
@@ -330,79 +384,68 @@ void jsonSetBool(jsonObject* bl, int val);
 
 jsonObject* jsonObjectClone( const jsonObject* o );
 
-
-/* tries to extract the string data from an object.
-       if object       -> NULL (the C NULL)
-       if array                ->      NULL  
-       if null         -> NULL 
-       if bool         -> NULL
-       if string/number the string version of either of those
-       The caller is responsible for freeing the returned string
-       */
 char* jsonObjectToSimpleString( const jsonObject* o );
 
-/**
- Allocate a buffer and format a specified numeric value into it,
- with up to 30 decimal digits of precision.   Caller is responsible
- for freeing the buffer.
- **/
 char* doubleToString( double num );
 
-/**
- Return 1 if the string is numeric, otherwise return 0.
- This validation follows the rules defined by the grammar at:
- http://www.json.org/
- **/
 int jsonIsNumeric( const char* s );
 
-/**
- Allocate and reformat a numeric string into one that is valid
- by JSON rules.  If the string is not numeric, return NULL.
- Caller is responsible for freeing the buffer.
- **/
 char* jsonScrubNumber( const char* s );
 
-/* provides an XPATH style search interface (e.g. /some/node/here) and 
-       return the object at that location if one exists.  Naturally,  
-       every element in the path must be a proper object ("hash" / {}).
-       Returns NULL if the specified node is not found 
-       Note also that the object returned is a clone and
-       must be freed by the caller
+/**
+       @brief Provide an XPATH style search interface to jsonObjects.
+       @param obj Pointer to the jsonObject to be searched.
+       @param path Pointer to a printf-style format string specifying the search path.  Subsequent
+               parameters, if any, are formatted and inserted into the formatted string.
+       @return A copy of the object at the specified location, if it exists, or NULL if it doesn't.
+
+       Example search path: /some/node/here.
+       
+       Every element in the path must be a proper object, a JSON_HASH.
+
+       The calling code is responsible for freeing the jsonObject to which the returned pointer
+       points.
 */
 jsonObject* jsonObjectFindPath( const jsonObject* obj, const char* path, ... );
 
 
-/* formats a JSON string from printing.  User must free returned string */
+/**
+       @brief Prettify a JSON string for printing, by adding newlines and other white space.
+       @param jsonString Pointer to the original JSON string.
+       @return Pointer to a prettified JSON string.
+
+       The calling code is responsible for freeing the returned string.
+*/
 char* jsonFormatString( const char* jsonString );
 
 /* sets the error handler for all parsers */
 void jsonSetGlobalErrorHandler(void (*errorHandler) (const char*));
 
 /* ------------------------------------------------------------------------- */
-/**
+/*
  * The following methods provide a facility for serializing and
  * deserializing "classed" JSON objects.  To give a JSON object a 
- * class, simply call jsonObjectSetClass().  
+ * class, simply call jsonObjectSetClass().
  * Then, calling jsonObjectEncodeClass() will convert the JSON
- * object (and any sub-objects) to a JSON object with class 
+ * object (and any sub-objects) to a JSON object with class
  * wrapper objects like so:
- * { _c : "classname", _d : <json_thing> }
- * In this example _c is the class key and _d is the data (object)
+ * { "__c" : "classname", "__p" : [json_thing] }
+ * In this example __c is the class key and __p is the data (object)
  * key.  The keys are defined by the constants 
- * OSRF_JSON_CLASS_KEY and OSRF_JSON_DATA_KEY
+ * JSON_CLASS_KEY and JSON_DATA_KEY
  * To revive a serialized object, simply call
  * jsonObjectDecodeClass()
  */
 
 
-/** Converts a class-wrapped object into an object with the
+/* Converts a class-wrapped object into an object with the
  * classname set
  * Caller must free the returned object 
  */ 
 jsonObject* jsonObjectDecodeClass( const jsonObject* obj );
 
 
-/** Converts an object with a classname into a
+/* Converts an object with a classname into a
  * class-wrapped (serialized) object
  * Caller must free the returned object 
  */ 
@@ -411,11 +454,10 @@ jsonObject* jsonObjectEncodeClass( const jsonObject* obj );
 /* ------------------------------------------------------------------------- */
 
 
-/**
+/*
  *     Generates an XML representation of a JSON object */
 char* jsonObjectToXML(const jsonObject*);
 
-
 /*
  * Builds a JSON object from the provided XML 
  */
index 0cab256..430cb32 100644 (file)
@@ -13,6 +13,17 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.
 */
 
+/**
+       @file osrf_json_object.c
+       @brief Implementation of the basic operations involving jsonObjects.
+
+       As a performance tweak: we maintain a free list of jsonObjects that have already
+       been allocated but are not currently in use.  When we need to create a jsonObject,
+       we can take one from the free list, if one is available, instead of calling
+       malloc().  Likewise when we free a jsonObject, we can stick it on the free list
+       for potential reuse instead of calling free().
+*/
+
 #include <stdlib.h>
 #include <ctype.h>
 #include <errno.h>
@@ -24,6 +35,19 @@ GNU General Public License for more details.
 
 /* cleans up an object if it is morphing another object, also
  * verifies that the appropriate storage container exists where appropriate */
+/**
+       @brief Coerce a jsonObj into a specified type, if it isn't already of that type.
+       @param _obj_ Pointer to the jsonObject to be coerced.
+       @param newtype The desired type.
+
+       If the old type and the new type don't match, discard and free the old contents.
+
+       If the old type is JSON_STRING or JSON_NUMBER, free the internal string buffer even
+       if the type is not changing.
+
+       If the new type is JSON_ARRAY or JSON_HASH, make sure there is an osrfList or osrfHash
+       in the jsonObject, respectively.
+*/
 #define JSON_INIT_CLEAR(_obj_, newtype)                \
        if( _obj_->type == JSON_HASH && newtype != JSON_HASH ) {                        \
                osrfHashFree(_obj_->value.h);                   \
@@ -46,11 +70,19 @@ GNU General Public License for more details.
                _obj_->value.l->freeItem = _jsonFreeListItem;\
        }
 
+/** Count of the times we put a freed jsonObject on the free list instead of calling free() */
 static int unusedObjCapture = 0;
+/** Count of the times we reused a jsonObject from the free list instead of calling malloc() */
 static int unusedObjRelease = 0;
+/** Count of the times we allocated a jsonObject with malloc() */
 static int mallocObjCreate = 0;
+/** Number of unused jsonObjects currently on the free list */
 static int currentListLen = 0;
 
+/**
+       Union overlaying a jsonObject with a pointer.  When the jsonObject is not in use as a
+       jsonObject, we use the overlaid pointer to maintain a linked list of unused jsonObjects.
+*/
 union unusedObjUnion{
 
        union unusedObjUnion* next;
@@ -58,19 +90,20 @@ union unusedObjUnion{
 };
 typedef union unusedObjUnion unusedObj;
 
-// We maintain a free list of jsonObjects that are available
-// for use, in order to reduce the churning through
-// malloc() and free().
-
+/** Pointer to the head of the free list */
 static unusedObj* freeObjList = NULL;
 
 static void add_json_to_buffer( const jsonObject* obj,
        growing_buffer * buf, int do_classname, int second_pass );
 
 /**
- * Return all unused jsonObjects to the heap
- * @return Nothing
- */
+       @brief Return all jsonObjects in the free list to the heap.
+
+       Reclaims memory occupied by unused jsonObjects in the free list.  It is never really
+       necessary to call this function, assuming that we don't run out of memory.  However
+       it might be worth calling if we have built and destroyed a lot of jsonObjects that
+       we don't expect to need again, in order to reduce our memory footprint.
+*/
 void jsonObjectFreeUnused( void ) {
 
        unusedObj* temp;
@@ -81,10 +114,22 @@ void jsonObjectFreeUnused( void ) {
        }
 }
 
+/**
+       @brief Create a new jsonObject, optionally containing a string.
+       @param data Pointer to a string to be stored in the jsonObject; may be NULL.
+       @return Pointer to a newly allocate jsonObject.
+
+       If @a data is NULL, create a jsonObject of type JSON_NULL.  Otherwise create
+       a jsonObject of type JSON_STRING, containing the specified string.
+
+       The calling code is responsible for freeing the jsonObject by calling jsonObjectFree().
+*/
 jsonObject* jsonNewObject(const char* data) {
 
        jsonObject* o;
 
+       // Allocate a jsonObject; from the free list if possible,
+       // or from the heap if necessary.
        if( freeObjList ) {
                o = (jsonObject*) freeObjList;
                freeObjList = freeObjList->next;
@@ -110,6 +155,17 @@ jsonObject* jsonNewObject(const char* data) {
        return o;
 }
 
+/**
+       @brief Create a jsonObject, optionally containing a formatted string.
+       @param data Pointer to a printf-style format string; may be NULL.  Subsequent parameters,
+       if any, will be formatted and inserted into the resulting string.
+       @return Pointer to a newly created jsonObject.
+
+       If @a data is NULL, create a jsonObject of type JSON_NULL, and ignore any extra parameters.
+       Otherwise create a jsonObject of type JSON_STRING, containing the formatted string.
+
+       The calling code is responsible for freeing the jsonObject by calling jsonObjectFree().
+ */
 jsonObject* jsonNewObjectFmt(const char* data, ...) {
 
        jsonObject* o;
@@ -141,6 +197,16 @@ jsonObject* jsonNewObjectFmt(const char* data, ...) {
        return o;
 }
 
+/**
+       @brief Create a new jsonObject of type JSON_NUMBER.
+       @param num The number to store in the jsonObject.
+       @return Pointer to the newly created jsonObject.
+
+       The number is stored internally as a character string, as formatted by
+       doubleToString().
+
+       The calling code is responsible for freeing the jsonObject by calling jsonObjectFree().
+*/
 jsonObject* jsonNewNumberObject( double num ) {
        jsonObject* o = jsonNewObject(NULL);
        o->type = JSON_NUMBER;
@@ -149,8 +215,15 @@ jsonObject* jsonNewNumberObject( double num ) {
 }
 
 /**
- * Creates a new number object from a numeric string
- */
+       @brief Create a new jsonObject of type JSON_NUMBER from a numeric string.
+       @param numstr Pointer to a numeric character string.
+       @return Pointer to a newly created jsonObject containing the numeric value, or NULL
+               if the string is not numeric.
+
+       The jsonIsNumeric() function determines whether the input string is numeric.
+
+       The calling code is responsible for freeing the jsonObject by calling jsonObjectFree().
+*/
 jsonObject* jsonNewNumberStringObject( const char* numstr ) {
        if( !numstr )
                numstr = "0";
@@ -163,6 +236,17 @@ jsonObject* jsonNewNumberStringObject( const char* numstr ) {
        return o;
 }
 
+/**
+       @brief Create a new jsonObject of type JSON_BOOL, with a specified boolean value.
+       @param val An int used as a boolean value.
+       @return Pointer to the new jsonObject.
+
+       Zero represents false, and non-zero represents true, according to the usual convention.
+       In practice the value of @a val is stored unchanged, but the calling code should not try to
+       take advantage of that fact, because future versions may behave differently.
+
+       The calling code is responsible for freeing the jsonObject by calling jsonObjectFree().
+*/
 jsonObject* jsonNewBoolObject(int val) {
     jsonObject* o = jsonNewObject(NULL);
     o->type = JSON_BOOL;
@@ -170,12 +254,35 @@ jsonObject* jsonNewBoolObject(int val) {
     return o;
 }
 
+/**
+       @brief Create a new jsonObject of a specified type, with a default value.
+       @param type One of the 6 JSON types, as specified by the JSON_* macros.
+       @return Pointer to the new jsonObject.
+
+       An invalid type parameter will go unnoticed, and may lead to unpleasantness.
+
+       The default value is equivalent to an empty string (for a JSON_STRING), zero (for a
+       JSON_NUMBER), an empty hash (for a JSON_HASH), an empty array (for a JSON_ARRAY), or
+       false (for a JSON_BOOL).  A JSON_NULL, of course, has no value, but can still be
+       useful.
+
+       The calling code is responsible for freeing the jsonObject by calling jsonObjectFree().
+*/
 jsonObject* jsonNewObjectType(int type) {
        jsonObject* o = jsonNewObject(NULL);
        o->type = type;
+       if( JSON_BOOL == type )
+               o->value.b = 0;
        return o;
 }
 
+/**
+       @brief Free a jsonObject and everything in it.
+       @param o Pointer to the jsonObject to be freed.
+
+       Any jsonObjects stored inside the jsonObject (in hashes or arrays) will be freed as
+       well, and so one, recursively.
+*/
 void jsonObjectFree( jsonObject* o ) {
 
        if(!o || o->parent) return;
@@ -204,12 +311,26 @@ void jsonObjectFree( jsonObject* o ) {
                        mallocObjCreate, unusedObjCapture, unusedObjRelease, currentListLen );
 }
 
+/**
+       @brief Free a jsonObject through a void pointer.
+       @param key Not used.
+       @param item Pointer to the jsonObject to be freed, cast to a void pointer.
+
+       This function is a callback for freeing jsonObjects stored in an osrfHash.
+*/
 static void _jsonFreeHashItem(char* key, void* item){
        if(!item) return;
        jsonObject* o = (jsonObject*) item;
        o->parent = NULL; /* detach the item */
        jsonObjectFree(o);
 }
+
+/**
+       @brief Free a jsonObject through a void pointer.
+       @param item Pointer to the jsonObject to be freed, cast to a void pointer.
+
+       This function is a callback for freeing jsonObjects stored in an osrfList.
+ */
 static void _jsonFreeListItem(void* item){
        if(!item) return;
        jsonObject* o = (jsonObject*) item;
@@ -217,12 +338,39 @@ static void _jsonFreeListItem(void* item){
        jsonObjectFree(o);
 }
 
+/**
+       @brief Assign a boolean value to a jsonObject of type JSON_BOOL.
+       @param bl Pointer to the jsonObject.
+       @param val The boolean value to be applied, encoded as an int.
+
+       If the jsonObject is not already of type JSON_BOOL, it is converted to one, and
+       any previous contents are freed.
+
+       Zero represents false, and non-zero represents true, according to the usual convention.
+       In practice the value of @a val is stored unchanged, but the calling code should not try to
+       take advantage of that fact, because future versions may behave differently.
+*/
 void jsonSetBool(jsonObject* bl, int val) {
     if(!bl) return;
     JSON_INIT_CLEAR(bl, JSON_BOOL);
     bl->value.b = val;
 }
 
+/**
+       @brief Insert one jsonObject into a jsonObect of type JSON_ARRAY, at the end of the array.
+       @param o Pointer to the outer jsonObject that will receive additional payload.
+       @param newo Pointer to the inner jsonObject, or NULL.
+       @return The number of jsonObjects directly subordinate to the outer jsonObject,
+               or -1 for error.
+
+       If the pointer to the outer jsonObject is NULL, jsonObjectPush returns -1.
+
+       If the outer jsonObject is not already of type JSON_ARRAY, it is converted to one, and
+       any previous contents are freed.
+
+       If the pointer to the inner jsonObject is NULL, jsonObjectPush creates a new jsonObject
+       of type JSON_NULL, and appends it to the array.
+*/
 unsigned long jsonObjectPush(jsonObject* o, jsonObject* newo) {
     if(!o) return -1;
     if(!newo) newo = jsonNewObject(NULL);
@@ -233,9 +381,31 @@ unsigned long jsonObjectPush(jsonObject* o, jsonObject* newo) {
        return o->size;
 }
 
+/**
+       @brief Insert a jsonObject at a specified position in a jsonObject of type JSON_ARRAY.
+       @param dest Pointer to the outer jsonObject that will receive the new payload.
+       @param index A zero-based subscript specifying the position of the new jsonObject.
+       @param newObj Pointer to the new jsonObject to be inserted, or NULL.
+       @return The size of the internal osrfList where the array is stored, or -1 on error.
+
+       If @a dest is NULL, jsonObjectSetIndex returns -1.
+
+       If the outer jsonObject is not already of type JSON_ARRAY, it is converted to one, and
+       any previous contents are freed.
+
+       If @a newObj is NULL, jsonObject creates a new jsonObject of type JSON_NULL and
+       inserts it into the array.
+
+       If there is already a jsonObject at the specified location, it is freed and replaced.
+
+       Depending on the placement of the inner jsonObject, it may leave unoccupied holes in the
+       array that are included in the reported size.  As a result of this and other peculiarities
+       of the underlying osrfList within the jsonObject, the reported size may not reflect the
+       number of jsonObjects in the array.  See osrf_list.c for further details.
+*/
 unsigned long jsonObjectSetIndex(jsonObject* dest, unsigned long index, jsonObject* newObj) {
-    if(!dest) return -1;
-    if(!newObj) newObj = jsonNewObject(NULL);
+       if(!dest) return -1;
+       if(!newObj) newObj = jsonNewObject(NULL);
        JSON_INIT_CLEAR(dest, JSON_ARRAY);
        newObj->parent = dest;
        osrfListSet( dest->value.l, newObj, index );
@@ -243,6 +413,26 @@ unsigned long jsonObjectSetIndex(jsonObject* dest, unsigned long index, jsonObje
        return dest->value.l->size;
 }
 
+/**
+       @brief Insert a jsonObject into a jsonObject of type JSON_HASH, for a specified key.
+       @param o Pointer to the outer jsonObject that will receive the new payload.
+       @param key Pointer to a string that will serve as the key.
+       @param newo Pointer to the new jsonObject that will be inserted, or NULL.
+       @return The number of items stored in the first level of the hash.
+
+       If the @a o parameter is NULL, jsonObjectSetKey returns -1.
+
+       If the outer jsonObject is not already of type JSON_HASH, it will be converted to one,
+       and any previous contents will be freed.
+
+       If @a key is NULL, jsonObjectSetKey returns the size of the hash without doing anything
+       (apart from possibly converting the outer jsonObject as described above).
+
+       If @a newo is NULL, jsonObjectSetKey creates a new jsonObject of type JSON_NULL and inserts
+       it with the specified key.
+
+       If a previous jsonObject is already stored with the same key, it is freed and replaced.
+*/
 unsigned long jsonObjectSetKey( jsonObject* o, const char* key, jsonObject* newo) {
     if(!o) return -1;
     if(!newo) newo = jsonNewObject(NULL);
@@ -253,31 +443,52 @@ unsigned long jsonObjectSetKey( jsonObject* o, const char* key, jsonObject* newo
        return o->size;
 }
 
+/**
+       @brief From a jsonObject of type JSON_HASH, find the inner jsonObject for a specified key.
+       @param obj Pointer to the outer jsonObject.
+       @param key The key for the associated item to be found.
+       @return A pointer to the inner jsonObject, if found, or NULL if not.
+
+       Returns NULL if either parameter is NULL, or if @a obj points to a jsonObject not of type
+       JSON_HASH, or if the specified key is not found in the outer jsonObject.
+
+       The returned pointer (if not NULL) points to the interior of the outer jsonObject.  The
+       calling code should @em not try to free it, but it may change its contents.
+*/
 jsonObject* jsonObjectGetKey( jsonObject* obj, const char* key ) {
        if(!(obj && obj->type == JSON_HASH && obj->value.h && key)) return NULL;
        return osrfHashGet( obj->value.h, key);
 }
 
+/**
+       @brief From a jsonObject of type JSON_HASH, find the inner jsonObject for a specified key.
+       @param obj Pointer to the outer jsonObject.
+       @param key The key for the associated item to be found.
+       @return A pointer to the inner jsonObject, if found, or NULL if not.
+
+       This function is identical to jsonObjectGetKey(), except that the outer jsonObject may be
+       const, and the pointer returned points to const.  As a result, the calling code is put on
+       notice that it should not try to modify the contents of the inner jsonObject.
+ */
 const jsonObject* jsonObjectGetKeyConst( const jsonObject* obj, const char* key ) {
        if(!(obj && obj->type == JSON_HASH && obj->value.h && key)) return NULL;
        return osrfHashGet( obj->value.h, key);
 }
 
 /**
- * Recursively traverse a jsonObject, formatting it into a JSON string.
- *
- * The last two parameters are booleans.
- *
- * If do_classname is true, examine each node for a classname, and if you
- * find one, pretend that the node is under an extra layer of JSON_HASH, with
- * JSON_CLASS_KEY and JSON_DATA_KEY as keys.
- *
- * second_pass should always be false except for some recursive calls.  It
- * is used when expanding classnames, to distinguish between the first and
- * second passes through a given node.
- *
- * @return Nothing
- */
+       @brief Recursively traverse a jsonObject, translating it into a JSON string.
+       @param obj Pointer to the jsonObject to be translated.
+       @param buf Pointer to a growing_buffer that will receive the JSON string.
+       @param do_classname Boolean; if true, expand class names.
+       @param second_pass Boolean; should always be false except for some recursive calls.
+       If @a do_classname is true, expand any class names, as described in the discussion of
+       jsonObjectToJSON().
+
+       @a second_pass should always be false except for some recursive calls.  It is used
+       when expanding classnames, to distinguish between the first and second passes
+       through a given node.
+*/
 static void add_json_to_buffer( const jsonObject* obj,
        growing_buffer * buf, int do_classname, int second_pass ) {
 
@@ -366,6 +577,13 @@ static void add_json_to_buffer( const jsonObject* obj,
        }
 }
 
+/**
+       @brief Translate a jsonObject into a JSON string, without expanding class names.
+       @param obj Pointer to the jsonObject to be translated.
+       @return A pointer to a newly allocated string containing the JSON.
+
+       The calling code is responsible for freeing the resulting string.
+*/
 char* jsonObjectToJSONRaw( const jsonObject* obj ) {
        if(!obj) return NULL;
        growing_buffer* buf = buffer_init(32);
@@ -373,6 +591,17 @@ char* jsonObjectToJSONRaw( const jsonObject* obj ) {
        return buffer_release( buf );
 }
 
+/**
+       @brief Translate a jsonObject into a JSON string, with expansion of class names.
+       @param obj Pointer to the jsonObject to be translated.
+       @return A pointer to a newly allocated string containing the JSON.
+
+       At every level, any jsonObject containing a class name will be translated as if it were
+       under an extra layer of JSON_HASH, with JSON_CLASS_KEY as the key for the class name and
+       JSON_DATA_KEY as the key for the jsonObject.
+
+       The calling code is responsible for freeing the resulting string.
+ */
 char* jsonObjectToJSON( const jsonObject* obj ) {
        if(!obj) return NULL;
        growing_buffer* buf = buffer_init(32);
@@ -380,6 +609,18 @@ char* jsonObjectToJSON( const jsonObject* obj ) {
        return buffer_release( buf );
 }
 
+/**
+       @brief Create a new jsonIterator for traversing a specified jsonObject.
+       @param obj Pointer to the jsonObject to be traversed.
+       @return A pointer to the newly allocated jsonIterator, or NULL upon error.
+
+       jsonNewIterator returns NULL if @a obj is NULL.
+
+       The new jsonIterator does not point to any particular position within the jsonObject to
+       be traversed.  The next call to jsonIteratorNext() will position it at the beginning.
+
+       The calling code is responsible for freeing the jsonIterator by calling jsonIteratorFree().
+*/
 jsonIterator* jsonNewIterator(const jsonObject* obj) {
        if(!obj) return NULL;
        jsonIterator* itr;
@@ -397,12 +638,42 @@ jsonIterator* jsonNewIterator(const jsonObject* obj) {
        return itr;
 }
 
+/**
+       @brief Free a jsonIterator and everything in it.
+       @param itr Pointer to the jsonIterator to be freed.
+*/
 void jsonIteratorFree(jsonIterator* itr) {
        if(!itr) return;
        osrfHashIteratorFree(itr->hashItr);
        free(itr);
 }
 
+/**
+       @brief Advance a jsonIterator to the next position within a jsonObject.
+       @param itr Pointer to the jsonIterator to be advanced.
+       @return A Pointer to the next jsonObject within the jsonObject being traversed; or NULL.
+
+       If the jsonObject being traversed is of type JSON_HASH, jsonIteratorNext returns a pointer
+       to the next jsonObject within the internal osrfHash.  The associated key string is available
+       via the pointer member itr->key.
+
+       If the jsonObject being traversed is of type JSON_ARRAY, jsonIteratorNext returns a pointer
+       to the next jsonObject within the internal osrfList.
+
+       In either case, the jsonIterator remains at the same level within the jsonObject that it is
+       traversing.  It does @em not descend to traverse deeper levels recursively.
+
+       If there is no next jsonObject within the jsonObject being traversed, jsonIteratorNext
+       returns NULL.  It also returns NULL if @a itr is NULL, or if the jsonIterator is
+       detectably corrupted, or if the jsonObject to be traversed is of a type other than
+       JSON_HASH or JSON_ARRAY.
+
+       Once jsonIteratorNext has returned NULL, subsequent calls using the same iterator will
+       continue to return NULL.  There is no available function to start over at the beginning.
+
+       The pointer returned, if not NULL, points to an internal element of the jsonObject being
+       traversed.  The calling code should @em not try to free it, but it may modify its contents.
+*/
 jsonObject* jsonIteratorNext(jsonIterator* itr) {
        if(!(itr && itr->obj)) return NULL;
        if( itr->obj->type == JSON_HASH ) {
@@ -420,6 +691,14 @@ jsonObject* jsonIteratorNext(jsonIterator* itr) {
        }
 }
 
+/**
+       @brief Determine whether a jsonIterator is positioned at the last element of a jsonObject.
+       @param itr Pointer to the jsonIterator whose position is to be tested.
+       @return An int, as boolean: 0 if the iterator is positioned at the end, or 1 if it isn't.
+
+       If the jsonIterator is positioned where a call to jsonIteratorNext() would return NULL,
+       then jsonIteratorHasNext returns 0.  Otherwise it returns 1.
+*/
 int jsonIteratorHasNext(const jsonIterator* itr) {
        if(!(itr && itr->obj)) return 0;
        if( itr->obj->type == JSON_HASH )
@@ -427,14 +706,34 @@ int jsonIteratorHasNext(const jsonIterator* itr) {
        return (itr->index < itr->obj->size) ? 1 : 0;
 }
 
+/**
+       @brief Fetch a pointer to a specified element within a jsonObject of type JSON_ARRAY.
+       @param obj Pointer to the outer jsonObject.
+       @param index A zero-based index identifying the element to be fetched.
+       @return A pointer to the element at the specified location, if any, or NULL.
+
+       The return value is NULL if @a obj is null, or if the outer jsonObject is not of type
+       JSON_ARRAY, or if there is no element at the specified location.
+
+       If not NULL, the pointer returned points to an element within the outer jsonObject.  The
+       calling code should @em not try to free it, but it may modify its contents.
+*/
 jsonObject* jsonObjectGetIndex( const jsonObject* obj, unsigned long index ) {
        if(!obj) return NULL;
        return (obj->type == JSON_ARRAY) ? 
         (OSRF_LIST_GET_INDEX(obj->value.l, index)) : NULL;
 }
 
-
-
+/**
+       @brief Remove a specified element from a jsonObject of type JSON_ARRAY.
+       @param dest Pointer to the jsonObject from which the element is to be removed.
+       @param index A zero-based index identifying the element to be removed.
+       @return The number of elements remaining at the top level, or -1 upon error.
+
+       The return value is -1 if @a dest is NULL, or if it points to a jsonObject not of type
+       JSON_ARRAY.  Otherwise it reflects the number of elements remaining in the top level of
+       the outer jsonObject, not counting any at lower levels.
+*/
 unsigned long jsonObjectRemoveIndex(jsonObject* dest, unsigned long index) {
        if( dest && dest->type == JSON_ARRAY ) {
                osrfListRemove(dest->value.l, index);
@@ -443,15 +742,41 @@ unsigned long jsonObjectRemoveIndex(jsonObject* dest, unsigned long index) {
        return -1;
 }
 
+/**
+       @brief Extract a specified element from a jsonObject of type JSON_ARRAY.
+       @param dest Pointer to the jsonObject from which the element is to be extracted.
+       @param index A zero-based index identifying the element to be extracted.
+       @return A pointer to the extracted element, if successful; otherwise NULL.
+
+       THe return value is NULL if @a dest is NULL, or if it points to a jsonObject not of type
+       JSON_ARRAY, or if there is no element at the specified location.
+
+       Otherwise, the calling code assumes ownership of the jsonObject to which the return value
+       points, and is responsible for freeing it by calling jsonObjectFree().  The original outer
+       jsonObject remains unchanged except for the removal of the specified element.
 
+       This function is sijmilar to jsonObjectRemoveIndex(), except that it returns a pointer to
+       the removed sub-object instead of destroying it.
+*/
 jsonObject* jsonObjectExtractIndex(jsonObject* dest, unsigned long index) {
        if( dest && dest->type == JSON_ARRAY ) {
-               return osrfListExtract(dest->value.l, index);
+               jsonObject* obj = osrfListExtract(dest->value.l, index);
+               if( obj )
+                       obj->parent = NULL;
+               return obj;
        } else
                return NULL;
 }
 
+/**
+       @brief Remove an element, specified by key, from a jsonObject of type JSON_HASH.
+       @param dest Pointer to the outer jsonObject from which an element is to be removed.
+       @param key The key for the associated element to be removed.
+       @return 1 if successful, or -1 if not.
 
+       The operation is considered successful if @a dest and @a key are both non-NULL, and
+       @a dest points to a jsonObject of type JSON_HASH, even if the specified key is not found.
+*/
 unsigned long jsonObjectRemoveKey( jsonObject* dest, const char* key) {
        if( dest && key && dest->type == JSON_HASH ) {
                osrfHashRemove(dest->value.h, key);
@@ -461,11 +786,17 @@ unsigned long jsonObjectRemoveKey( jsonObject* dest, const char* key) {
 }
 
 /**
- Allocate a buffer and format a specified numeric value into it.
- Caller is responsible for freeing the buffer.
-**/
+       @brief Format a double into a character string.
+       @param num The double to be formatted.
+       @return A newly allocated character string containing the formatted number.
+
+       The number is formatted according to the printf-style format specification "%.30g". and
+       will therefore contain no more than 30 significant digits.
+
+       The calling code is responsible for freeing the resulting string.
+*/
 char* doubleToString( double num ) {
-       
+
        char buf[ 64 ];
        size_t len = snprintf(buf, sizeof( buf ), "%.30g", num) + 1;
        if( len < sizeof( buf ) )
@@ -480,6 +811,19 @@ char* doubleToString( double num ) {
        }
 }
 
+/**
+       @brief Fetch a pointer to the string stored in a jsonObject, if any.
+       @param obj Pointer to the jsonObject.
+       @return Pointer to the string stored internally, or NULL.
+
+       If @a obj points to a jsonObject of type JSON_STRING or JSON_NUMBER, the returned value
+       points to the string stored internally (a numeric string in the case of a JSON_NUMBER).
+       Otherwise the returned value is NULL.
+
+       The returned pointer should be treated as a pointer to const.  In particular it should
+       @em not be freed.  In a future release, the returned pointer may indeed be a pointer
+       to const.
+*/
 char* jsonObjectGetString(const jsonObject* obj) {
        if(obj)
        {
@@ -494,11 +838,29 @@ char* jsonObjectGetString(const jsonObject* obj) {
                return NULL;
 }
 
+/**
+       @brief Translate a jsonObject to a double.
+       @param @obj Pointer to the jsonObject.
+       @return The numeric value stored in the jsonObject.
+
+       If @a obj is NULL, or if it points to a jsonObject not of type JSON_NUMBER, the value
+       returned is zero.
+*/
 double jsonObjectGetNumber( const jsonObject* obj ) {
        return (obj && obj->type == JSON_NUMBER && obj->value.s)
                        ? strtod( obj->value.s, NULL ) : 0;
 }
 
+/**
+       @brief Store a copy of a specified character string in a jsonObject of type JSON_STRING.
+       @param dest Pointer to the jsonObject in which the string will be stored.
+       @param string Pointer to the string to be stored.
+
+       Both @a dest and @a string must be non-NULL.
+
+       If the jsonObject is not already of type JSON_STRING, it is converted to a JSON_STRING,
+       with any previous contents freed.
+*/
 void jsonObjectSetString(jsonObject* dest, const char* string) {
        if(!(dest && string)) return;
        JSON_INIT_CLEAR(dest, JSON_STRING);
@@ -510,6 +872,19 @@ void jsonObjectSetString(jsonObject* dest, const char* string) {
  a specified numeric string in it.  If the string is not numeric,
  store the equivalent of zero, and return an error status.
 **/
+/**
+       @brief Store a copy of a numeric character string in a jsonObject of type JSON_NUMBER.
+       @param dest Pointer to the jsonObject in which the number will be stored.
+       @param string Pointer to the numeric string to be stored.
+
+       Both @a dest and @a string must be non-NULL.
+
+       If the jsonObject is not already of type JSON_NUMBER, it is converted to a JSON_STRING,
+       with any previous contents freed.
+
+       If the input string is not numeric as determined by jsonIsNumeric(), the number stored
+       is zero.
+ */
 int jsonObjectSetNumberString(jsonObject* dest, const char* string) {
        if(!(dest && string)) return -1;
        JSON_INIT_CLEAR(dest, JSON_NUMBER);
@@ -524,22 +899,53 @@ int jsonObjectSetNumberString(jsonObject* dest, const char* string) {
        }
 }
 
+/**
+       @brief Store a number in a jsonObject of type JSON_NUMBER.
+       @param dest Pointer to the jsonObject in which the number will be stored.
+       @param num The number to be stored.
+
+       If the jsonObject is not already of type JSON_NUMBER, it is converted to one, with any
+       previous contents freed.
+*/
 void jsonObjectSetNumber(jsonObject* dest, double num) {
        if(!dest) return;
        JSON_INIT_CLEAR(dest, JSON_NUMBER);
        dest->value.s = doubleToString( num );
 }
 
+/**
+       @brief Assign a class name to a jsonObject.
+       @param dest Pointer to the jsonObject.
+       @param classname Pointer to a string containing the class name.
+
+       Both dest and classname must be non-NULL.
+*/
 void jsonObjectSetClass(jsonObject* dest, const char* classname ) {
        if(!(dest && classname)) return;
        free(dest->classname);
        dest->classname = strdup(classname);
 }
+
+/**
+       @brief Fetch a pointer to the class name of a jsonObject, if any.
+       @param dest Pointer to the jsonObject.
+       @return Pointer to a string containing the class name, if there is one; or NULL.
+
+       If not NULL, the pointer returned points to a string stored internally within the
+       jsonObject.  The calling code should @em not try to free it.
+*/
 const char* jsonObjectGetClass(const jsonObject* dest) {
     if(!dest) return NULL;
     return dest->classname;
 }
 
+/**
+       @brief Create a copy of an existing jsonObject, including all internal sub-objects.
+       @param o Pointer to the jsonObject to be copied.
+       @return A pointer to the newly created copy.
+
+       The calling code is responsible for freeing the copy of the original.
+*/
 jsonObject* jsonObjectClone( const jsonObject* o ) {
     if(!o) return jsonNewObject(NULL);
 
@@ -586,6 +992,14 @@ jsonObject* jsonObjectClone( const jsonObject* o ) {
     return result;
 }
 
+/**
+       @brief Return the truth or falsity of a jsonObject of type JSON_BOOL.
+       @param boolObj Pointer to the jsonObject.
+       @return 1 or 0, depending on whether the stored boolean is true or false.
+
+       If @a boolObj is NULL, or if it points to a jsonObject not of type JSON_BOOL, the
+       returned value is zero.
+*/
 int jsonBoolIsTrue( const jsonObject* boolObj ) {
     if( boolObj && boolObj->type == JSON_BOOL && boolObj->value.b )
         return 1;
@@ -593,6 +1007,16 @@ int jsonBoolIsTrue( const jsonObject* boolObj ) {
 }
 
 
+/**
+       @brief Create a copy of the string stored in a jsonObject.
+       @param o Pointer to the jsonObject whose string is to be copied.
+       @return Pointer to a newly allocated string copied from the jsonObject.
+
+       If @a o is NULL, or if it points to a jsonObject not of type JSON_STRING or JSON_NUMBER,
+       the returned value is NULL.  In the case of a JSON_NUMBER, the string created is numeric.
+
+       The calling code is responsible for freeing the newly created string.
+*/
 char* jsonObjectToSimpleString( const jsonObject* o ) {
        if(!o) return NULL;
 
@@ -616,6 +1040,29 @@ char* jsonObjectToSimpleString( const jsonObject* o ) {
  This validation follows the rules defined by the grammar at:
  http://www.json.org/
  **/
+/**
+       @brief Determine whether a specified character string is a valid JSON number.
+       @param s Pointer to the string to be examined.
+       @return 1 if the string is numeric, or 0 if not.
+
+       This function defines numericity according to JSON rules; see http://json.org/.  This
+       determination is based purely on the lexical properties of the string.  In particular
+       there is no guarantee that the number in a numeric string is representable in C as a
+       long long, a long double, or any other built-in type.
+
+       A numeric string consists of:
+
+       - An optional leading minus sign (but not a plus sign)
+       - One or more decimal digits.  The first digit may be a zero only if it is the only
+         digit to the left of the decimal.
+       - Optionally, a decimal point followed by one or more decimal digits.
+       - An optional exponent, consisting of:
+               - The letter E, in upper or lower case
+               - An optional plus or minus sign
+               - One or more decimal digits
+
+       See also jsonScrubNumber().
+*/
 int jsonIsNumeric( const char* s ) {
 
        if( !s || !*s ) return 0;
@@ -699,10 +1146,23 @@ int jsonIsNumeric( const char* s ) {
 }
 
 /**
- Allocate and reformat a numeric string into one that is valid
- by JSON rules.  If the string is not numeric, return NULL.
- Caller is responsible for freeing the buffer.
- **/
+       @brief Edit a string into a valid JSON number, if possible.
+       @param s Pointer to the string to be edited.
+       @return A pointer to a newly created numeric string, if possible; otherwise NULL.
+
+       JSON has rather exacting requirements about what constitutes a valid numeric string (see
+       jsonIsNumeric()).  Real-world input may be a bit sloppy.  jsonScrubNumber accepts numeric
+       strings in a less formal format and reformats them, where possible, according to JSON
+       rules.  It removes leading white space, a leading plus sign, and extraneous leading zeros.
+       It adds a leading zero as needed when the absolute value is less than 1.  It also accepts
+       scientific notation in the form of a bare exponent (e.g. "E-3"), supplying a leading factor
+       of "1".
+
+       If the input string is non-numeric even according to these relaxed rules, the return value
+       is NULL.
+
+       The calling code is responsible for freeing the newly created numeric string.
+*/
 char* jsonScrubNumber( const char* s ) {
        if( !s || !*s ) return NULL;