[Opensrf-commits] r1794 - in trunk: include/opensrf src/libopensrf (scottmk)
svn at svn.open-ils.org
svn at svn.open-ils.org
Fri Sep 25 10:35:21 EDT 2009
Author: scottmk
Date: 2009-09-25 10:35:18 -0400 (Fri, 25 Sep 2009)
New Revision: 1794
Modified:
trunk/include/opensrf/osrf_json.h
trunk/src/libopensrf/osrf_json_object.c
Log:
1. Add doxygen markup for documentation.
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
Modified: trunk/include/opensrf/osrf_json.h
===================================================================
--- trunk/include/opensrf/osrf_json.h 2009-09-18 12:57:58 UTC (rev 1793)
+++ trunk/include/opensrf/osrf_json.h 2009-09-25 14:35:18 UTC (rev 1794)
@@ -13,6 +13,38 @@
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 @@
#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 @@
#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 @@
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, ... );
+jsonObject* jsonNewObject(const char* data);
-
-/**
- * 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 );
+jsonObject* jsonObjectGetKey( jsonObject* obj, const char* key );
-/**
- * Retrieves the object at the given key
- */
-jsonObject* jsonObjectGetKey( 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 @@
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 @@
/* ------------------------------------------------------------------------- */
-/**
+/*
* Generates an XML representation of a JSON object */
char* jsonObjectToXML(const jsonObject*);
-
/*
* Builds a JSON object from the provided XML
*/
Modified: trunk/src/libopensrf/osrf_json_object.c
===================================================================
--- trunk/src/libopensrf/osrf_json_object.c 2009-09-18 12:57:58 UTC (rev 1793)
+++ trunk/src/libopensrf/osrf_json_object.c 2009-09-25 14:35:18 UTC (rev 1794)
@@ -13,6 +13,17 @@
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 @@
/* 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 @@
_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 @@
};
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 @@
}
}
+/**
+ @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 @@
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 @@
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 @@
}
/**
- * 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
}
}
+/**
+ @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 @@
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 @@
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 @@
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 @@
}
}
+/**
+ @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 @@
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 @@
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 @@
}
/**
- 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 @@
}
}
+/**
+ @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 @@
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 @@
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 @@
}
}
+/**
+ @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 @@
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 @@
}
+/**
+ @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 @@
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 @@
}
/**
- 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;
More information about the opensrf-commits
mailing list