2 Copyright (C) 2006 Georgia Public Library Service
3 Bill Erickson <billserickson@gmail.com>
5 This program is free software; you can redistribute it and/or
6 modify it under the terms of the GNU General Public License
7 as published by the Free Software Foundation; either version 2
8 of the License, or (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
17 @file osrf_json_object.c
18 @brief Implementation of the basic operations involving jsonObjects.
20 As a performance tweak: we maintain a free list of jsonObjects that have already
21 been allocated but are not currently in use. When we need to create a jsonObject,
22 we can take one from the free list, if one is available, instead of calling
23 malloc(). Likewise when we free a jsonObject, we can stick it on the free list
24 for potential reuse instead of calling free().
31 #include <opensrf/log.h>
32 #include <opensrf/osrf_json.h>
33 #include <opensrf/osrf_json_utils.h>
34 #include <opensrf/osrf_utf8.h>
36 /* cleans up an object if it is morphing another object, also
37 * verifies that the appropriate storage container exists where appropriate */
39 @brief Coerce a jsonObj into a specified type, if it isn't already of that type.
40 @param _obj_ Pointer to the jsonObject to be coerced.
41 @param newtype The desired type.
43 If the old type and the new type don't match, discard and free the old contents.
45 If the old type is JSON_STRING or JSON_NUMBER, free the internal string buffer even
46 if the type is not changing.
48 If the new type is JSON_ARRAY or JSON_HASH, make sure there is an osrfList or osrfHash
49 in the jsonObject, respectively.
51 #define JSON_INIT_CLEAR(_obj_, newtype) \
52 if( _obj_->type == JSON_HASH && newtype != JSON_HASH ) { \
53 osrfHashFree(_obj_->value.h); \
54 _obj_->value.h = NULL; \
55 } else if( _obj_->type == JSON_ARRAY && newtype != JSON_ARRAY ) { \
56 osrfListFree(_obj_->value.l); \
57 _obj_->value.l = NULL; \
58 } else if( _obj_->type == JSON_STRING || _obj_->type == JSON_NUMBER ) { \
59 free(_obj_->value.s); \
60 _obj_->value.s = NULL; \
61 } else if( _obj_->type == JSON_BOOL && newtype != JSON_BOOL ) { \
62 _obj_->value.l = NULL; \
64 _obj_->type = newtype; \
65 if( newtype == JSON_HASH && _obj_->value.h == NULL ) { \
66 _obj_->value.h = osrfNewHash(); \
67 osrfHashSetCallback( _obj_->value.h, _jsonFreeHashItem ); \
68 } else if( newtype == JSON_ARRAY && _obj_->value.l == NULL ) { \
69 _obj_->value.l = osrfNewList(); \
70 _obj_->value.l->freeItem = _jsonFreeListItem;\
73 /** Count of the times we put a freed jsonObject on the free list instead of calling free() */
74 static int unusedObjCapture = 0;
75 /** Count of the times we reused a jsonObject from the free list instead of calling malloc() */
76 static int unusedObjRelease = 0;
77 /** Count of the times we allocated a jsonObject with malloc() */
78 static int mallocObjCreate = 0;
79 /** Number of unused jsonObjects currently on the free list */
80 static int currentListLen = 0;
83 Union overlaying a jsonObject with a pointer. When the jsonObject is not in use as a
84 jsonObject, we use the overlaid pointer to maintain a linked list of unused jsonObjects.
88 union unusedObjUnion* next;
91 typedef union unusedObjUnion unusedObj;
93 /** Pointer to the head of the free list */
94 static unusedObj* freeObjList = NULL;
96 static void add_json_to_buffer( const jsonObject* obj,
97 growing_buffer * buf, int do_classname, int second_pass );
100 @brief Return all jsonObjects in the free list to the heap.
102 Reclaims memory occupied by unused jsonObjects in the free list. It is never really
103 necessary to call this function, assuming that we don't run out of memory. However
104 it might be worth calling if we have built and destroyed a lot of jsonObjects that
105 we don't expect to need again, in order to reduce our memory footprint.
107 void jsonObjectFreeUnused( void ) {
110 while( freeObjList ) {
111 temp = freeObjList->next;
118 @brief Create a new jsonObject, optionally containing a string.
119 @param data Pointer to a string to be stored in the jsonObject; may be NULL.
120 @return Pointer to a newly allocate jsonObject.
122 If @a data is NULL, create a jsonObject of type JSON_NULL. Otherwise create
123 a jsonObject of type JSON_STRING, containing the specified string.
125 The calling code is responsible for freeing the jsonObject by calling jsonObjectFree().
127 jsonObject* jsonNewObject(const char* data) {
131 // Allocate a jsonObject; from the free list if possible,
132 // or from the heap if necessary.
134 o = (jsonObject*) freeObjList;
135 freeObjList = freeObjList->next;
139 OSRF_MALLOC( o, sizeof(jsonObject) );
148 o->type = JSON_STRING;
149 o->value.s = strdup(data);
159 @brief Create a jsonObject, optionally containing a formatted string.
160 @param data Pointer to a printf-style format string; may be NULL. Subsequent parameters,
161 if any, will be formatted and inserted into the resulting string.
162 @return Pointer to a newly created jsonObject.
164 If @a data is NULL, create a jsonObject of type JSON_NULL, and ignore any extra parameters.
165 Otherwise create a jsonObject of type JSON_STRING, containing the formatted string.
167 The calling code is responsible for freeing the jsonObject by calling jsonObjectFree().
169 jsonObject* jsonNewObjectFmt(const char* data, ...) {
174 o = (jsonObject*) freeObjList;
175 freeObjList = freeObjList->next;
179 OSRF_MALLOC( o, sizeof(jsonObject) );
188 VA_LIST_TO_STRING(data);
189 o->type = JSON_STRING;
190 o->value.s = strdup(VA_BUF);
201 @brief Create a new jsonObject of type JSON_NUMBER.
202 @param num The number to store in the jsonObject.
203 @return Pointer to the newly created jsonObject.
205 The number is stored internally as a character string, as formatted by
208 The calling code is responsible for freeing the jsonObject by calling jsonObjectFree().
210 jsonObject* jsonNewNumberObject( double num ) {
211 jsonObject* o = jsonNewObject(NULL);
212 o->type = JSON_NUMBER;
213 o->value.s = doubleToString( num );
218 @brief Create a new jsonObject of type JSON_NUMBER from a numeric string.
219 @param numstr Pointer to a numeric character string.
220 @return Pointer to a newly created jsonObject containing the numeric value, or NULL
221 if the string is not numeric.
223 The jsonIsNumeric() function determines whether the input string is numeric.
225 The calling code is responsible for freeing the jsonObject by calling jsonObjectFree().
227 jsonObject* jsonNewNumberStringObject( const char* numstr ) {
230 else if( !jsonIsNumeric( numstr ) )
233 jsonObject* o = jsonNewObject(NULL);
234 o->type = JSON_NUMBER;
235 o->value.s = strdup( numstr );
240 @brief Create a new jsonObject of type JSON_BOOL, with a specified boolean value.
241 @param val An int used as a boolean value.
242 @return Pointer to the new jsonObject.
244 Zero represents false, and non-zero represents true, according to the usual convention.
245 In practice the value of @a val is stored unchanged, but the calling code should not try to
246 take advantage of that fact, because future versions may behave differently.
248 The calling code is responsible for freeing the jsonObject by calling jsonObjectFree().
250 jsonObject* jsonNewBoolObject(int val) {
251 jsonObject* o = jsonNewObject(NULL);
258 @brief Create a new jsonObject of a specified type, with a default value.
259 @param type One of the 6 JSON types, as specified by the JSON_* macros.
260 @return Pointer to the new jsonObject.
262 An invalid type parameter will go unnoticed, and may lead to unpleasantness.
264 The default value is equivalent to an empty string (for a JSON_STRING), zero (for a
265 JSON_NUMBER), an empty hash (for a JSON_HASH), an empty array (for a JSON_ARRAY), or
266 false (for a JSON_BOOL). A JSON_NULL, of course, has no value, but can still be
269 The calling code is responsible for freeing the jsonObject by calling jsonObjectFree().
271 jsonObject* jsonNewObjectType(int type) {
272 jsonObject* o = jsonNewObject(NULL);
274 if( JSON_BOOL == type )
280 @brief Free a jsonObject and everything in it.
281 @param o Pointer to the jsonObject to be freed.
283 Any jsonObjects stored inside the jsonObject (in hashes or arrays) will be freed as
284 well, and so one, recursively.
286 void jsonObjectFree( jsonObject* o ) {
288 if(!o || o->parent) return;
292 case JSON_HASH : osrfHashFree(o->value.h); break;
293 case JSON_ARRAY : osrfListFree(o->value.l); break;
294 case JSON_STRING : free(o->value.s); break;
295 case JSON_NUMBER : free(o->value.s); break;
298 // Stick the old jsonObject onto a free list
299 // for potential reuse
301 unusedObj* unused = (unusedObj*) o;
302 unused->next = freeObjList;
303 freeObjList = unused;
307 if (unusedObjCapture > 1 && !(unusedObjCapture % 1000))
308 osrfLogDebug( OSRF_LOG_MARK, "Objects malloc()'d: %d, "
309 "Reusable objects captured: %d, Objects reused: %d, "
310 "Current List Length: %d",
311 mallocObjCreate, unusedObjCapture, unusedObjRelease, currentListLen );
315 @brief Free a jsonObject through a void pointer.
317 @param item Pointer to the jsonObject to be freed, cast to a void pointer.
319 This function is a callback for freeing jsonObjects stored in an osrfHash.
321 static void _jsonFreeHashItem(char* key, void* item){
323 jsonObject* o = (jsonObject*) item;
324 o->parent = NULL; /* detach the item */
329 @brief Free a jsonObject through a void pointer.
330 @param item Pointer to the jsonObject to be freed, cast to a void pointer.
332 This function is a callback for freeing jsonObjects stored in an osrfList.
334 static void _jsonFreeListItem(void* item){
336 jsonObject* o = (jsonObject*) item;
337 o->parent = NULL; /* detach the item */
342 @brief Assign a boolean value to a jsonObject of type JSON_BOOL.
343 @param bl Pointer to the jsonObject.
344 @param val The boolean value to be applied, encoded as an int.
346 If the jsonObject is not already of type JSON_BOOL, it is converted to one, and
347 any previous contents are freed.
349 Zero represents false, and non-zero represents true, according to the usual convention.
350 In practice the value of @a val is stored unchanged, but the calling code should not try to
351 take advantage of that fact, because future versions may behave differently.
353 void jsonSetBool(jsonObject* bl, int val) {
355 JSON_INIT_CLEAR(bl, JSON_BOOL);
360 @brief Insert one jsonObject into a jsonObect of type JSON_ARRAY, at the end of the array.
361 @param o Pointer to the outer jsonObject that will receive additional payload.
362 @param newo Pointer to the inner jsonObject, or NULL.
363 @return The number of jsonObjects directly subordinate to the outer jsonObject,
366 If the pointer to the outer jsonObject is NULL, jsonObjectPush returns -1.
368 If the outer jsonObject is not already of type JSON_ARRAY, it is converted to one, and
369 any previous contents are freed.
371 If the pointer to the inner jsonObject is NULL, jsonObjectPush creates a new jsonObject
372 of type JSON_NULL, and appends it to the array.
374 unsigned long jsonObjectPush(jsonObject* o, jsonObject* newo) {
376 if(!newo) newo = jsonNewObject(NULL);
377 JSON_INIT_CLEAR(o, JSON_ARRAY);
379 osrfListPush( o->value.l, newo );
380 o->size = o->value.l->size;
385 @brief Insert a jsonObject at a specified position in a jsonObject of type JSON_ARRAY.
386 @param dest Pointer to the outer jsonObject that will receive the new payload.
387 @param index A zero-based subscript specifying the position of the new jsonObject.
388 @param newObj Pointer to the new jsonObject to be inserted, or NULL.
389 @return The size of the internal osrfList where the array is stored, or -1 on error.
391 If @a dest is NULL, jsonObjectSetIndex returns -1.
393 If the outer jsonObject is not already of type JSON_ARRAY, it is converted to one, and
394 any previous contents are freed.
396 If @a newObj is NULL, jsonObject creates a new jsonObject of type JSON_NULL and
397 inserts it into the array.
399 If there is already a jsonObject at the specified location, it is freed and replaced.
401 Depending on the placement of the inner jsonObject, it may leave unoccupied holes in the
402 array that are included in the reported size. As a result of this and other peculiarities
403 of the underlying osrfList within the jsonObject, the reported size may not reflect the
404 number of jsonObjects in the array. See osrf_list.c for further details.
406 unsigned long jsonObjectSetIndex(jsonObject* dest, unsigned long index, jsonObject* newObj) {
408 if(!newObj) newObj = jsonNewObject(NULL);
409 JSON_INIT_CLEAR(dest, JSON_ARRAY);
410 newObj->parent = dest;
411 osrfListSet( dest->value.l, newObj, index );
412 dest->size = dest->value.l->size;
413 return dest->value.l->size;
417 @brief Insert a jsonObject into a jsonObject of type JSON_HASH, for a specified key.
418 @param o Pointer to the outer jsonObject that will receive the new payload.
419 @param key Pointer to a string that will serve as the key.
420 @param newo Pointer to the new jsonObject that will be inserted, or NULL.
421 @return The number of items stored in the first level of the hash.
423 If the @a o parameter is NULL, jsonObjectSetKey returns -1.
425 If the outer jsonObject is not already of type JSON_HASH, it will be converted to one,
426 and any previous contents will be freed.
428 If @a key is NULL, jsonObjectSetKey returns the size of the hash without doing anything
429 (apart from possibly converting the outer jsonObject as described above).
431 If @a newo is NULL, jsonObjectSetKey creates a new jsonObject of type JSON_NULL and inserts
432 it with the specified key.
434 If a previous jsonObject is already stored with the same key, it is freed and replaced.
436 unsigned long jsonObjectSetKey( jsonObject* o, const char* key, jsonObject* newo) {
438 if(!newo) newo = jsonNewObject(NULL);
439 JSON_INIT_CLEAR(o, JSON_HASH);
441 osrfHashSet( o->value.h, newo, key );
442 o->size = osrfHashGetCount(o->value.h);
447 @brief From a jsonObject of type JSON_HASH, find the inner jsonObject for a specified key.
448 @param obj Pointer to the outer jsonObject.
449 @param key The key for the associated item to be found.
450 @return A pointer to the inner jsonObject, if found, or NULL if not.
452 Returns NULL if either parameter is NULL, or if @a obj points to a jsonObject not of type
453 JSON_HASH, or if the specified key is not found in the outer jsonObject.
455 The returned pointer (if not NULL) points to the interior of the outer jsonObject. The
456 calling code should @em not try to free it, but it may change its contents.
458 jsonObject* jsonObjectGetKey( jsonObject* obj, const char* key ) {
459 if(!(obj && obj->type == JSON_HASH && obj->value.h && key)) return NULL;
460 return osrfHashGet( obj->value.h, key);
464 @brief From a jsonObject of type JSON_HASH, find the inner jsonObject for a specified key.
465 @param obj Pointer to the outer jsonObject.
466 @param key The key for the associated item to be found.
467 @return A pointer to the inner jsonObject, if found, or NULL if not.
469 This function is identical to jsonObjectGetKey(), except that the outer jsonObject may be
470 const, and the pointer returned points to const. As a result, the calling code is put on
471 notice that it should not try to modify the contents of the inner jsonObject.
473 const jsonObject* jsonObjectGetKeyConst( const jsonObject* obj, const char* key ) {
474 if(!(obj && obj->type == JSON_HASH && obj->value.h && key)) return NULL;
475 return osrfHashGet( obj->value.h, key);
479 @brief Recursively traverse a jsonObject, translating it into a JSON string.
480 @param obj Pointer to the jsonObject to be translated.
481 @param buf Pointer to a growing_buffer that will receive the JSON string.
482 @param do_classname Boolean; if true, expand class names.
483 @param second_pass Boolean; should always be false except for some recursive calls.
485 If @a do_classname is true, expand any class names, as described in the discussion of
488 @a second_pass should always be false except for some recursive calls. It is used
489 when expanding classnames, to distinguish between the first and second passes
490 through a given node.
492 static void add_json_to_buffer( const jsonObject* obj,
493 growing_buffer * buf, int do_classname, int second_pass ) {
496 OSRF_BUFFER_ADD(buf, "null");
500 if( obj->classname && do_classname )
506 // Pretend we see an extra layer of JSON_HASH
508 OSRF_BUFFER_ADD( buf, "{\"" );
509 OSRF_BUFFER_ADD( buf, JSON_CLASS_KEY );
510 OSRF_BUFFER_ADD( buf, "\":\"" );
511 OSRF_BUFFER_ADD( buf, obj->classname );
512 OSRF_BUFFER_ADD( buf, "\",\"" );
513 OSRF_BUFFER_ADD( buf, JSON_DATA_KEY );
514 OSRF_BUFFER_ADD( buf, "\":" );
515 add_json_to_buffer( obj, buf, 1, 1 );
516 buffer_add_char( buf, '}' );
524 if(obj->value.b) OSRF_BUFFER_ADD(buf, "true");
525 else OSRF_BUFFER_ADD(buf, "false");
529 if(obj->value.s) OSRF_BUFFER_ADD( buf, obj->value.s );
530 else OSRF_BUFFER_ADD_CHAR( buf, '0' );
535 OSRF_BUFFER_ADD(buf, "null");
539 OSRF_BUFFER_ADD_CHAR(buf, '"');
540 buffer_append_utf8(buf, obj->value.s);
541 OSRF_BUFFER_ADD_CHAR(buf, '"');
545 OSRF_BUFFER_ADD_CHAR(buf, '[');
548 for( i = 0; i != obj->value.l->size; i++ ) {
549 if(i > 0) OSRF_BUFFER_ADD(buf, ",");
551 OSRF_LIST_GET_INDEX(obj->value.l, i), buf, do_classname, second_pass );
554 OSRF_BUFFER_ADD_CHAR(buf, ']');
560 OSRF_BUFFER_ADD_CHAR(buf, '{');
561 osrfHashIterator* itr = osrfNewHashIterator(obj->value.h);
565 while( (item = osrfHashIteratorNext(itr)) ) {
566 if(i++ > 0) OSRF_BUFFER_ADD_CHAR(buf, ',');
567 OSRF_BUFFER_ADD_CHAR(buf, '"');
568 buffer_append_utf8(buf, osrfHashIteratorKey(itr));
569 OSRF_BUFFER_ADD(buf, "\":");
570 add_json_to_buffer( item, buf, do_classname, second_pass );
573 osrfHashIteratorFree(itr);
574 OSRF_BUFFER_ADD_CHAR(buf, '}');
581 @brief Translate a jsonObject into a JSON string, without expanding class names.
582 @param obj Pointer to the jsonObject to be translated.
583 @return A pointer to a newly allocated string containing the JSON.
585 The calling code is responsible for freeing the resulting string.
587 char* jsonObjectToJSONRaw( const jsonObject* obj ) {
588 if(!obj) return NULL;
589 growing_buffer* buf = buffer_init(32);
590 add_json_to_buffer( obj, buf, 0, 0 );
591 return buffer_release( buf );
595 @brief Translate a jsonObject into a JSON string, with expansion of class names.
596 @param obj Pointer to the jsonObject to be translated.
597 @return A pointer to a newly allocated string containing the JSON.
599 At every level, any jsonObject containing a class name will be translated as if it were
600 under an extra layer of JSON_HASH, with JSON_CLASS_KEY as the key for the class name and
601 JSON_DATA_KEY as the key for the jsonObject.
603 The calling code is responsible for freeing the resulting string.
605 char* jsonObjectToJSON( const jsonObject* obj ) {
606 if(!obj) return NULL;
607 growing_buffer* buf = buffer_init(32);
608 add_json_to_buffer( obj, buf, 1, 0 );
609 return buffer_release( buf );
613 @brief Create a new jsonIterator for traversing a specified jsonObject.
614 @param obj Pointer to the jsonObject to be traversed.
615 @return A pointer to the newly allocated jsonIterator, or NULL upon error.
617 jsonNewIterator returns NULL if @a obj is NULL.
619 The new jsonIterator does not point to any particular position within the jsonObject to
620 be traversed. The next call to jsonIteratorNext() will position it at the beginning.
622 The calling code is responsible for freeing the jsonIterator by calling jsonIteratorFree().
624 jsonIterator* jsonNewIterator(const jsonObject* obj) {
625 if(!obj) return NULL;
627 OSRF_MALLOC(itr, sizeof(jsonIterator));
629 itr->obj = (jsonObject*) obj;
633 if( obj->type == JSON_HASH )
634 itr->hashItr = osrfNewHashIterator(obj->value.h);
642 @brief Free a jsonIterator and everything in it.
643 @param itr Pointer to the jsonIterator to be freed.
645 void jsonIteratorFree(jsonIterator* itr) {
647 osrfHashIteratorFree(itr->hashItr);
652 @brief Advance a jsonIterator to the next position within a jsonObject.
653 @param itr Pointer to the jsonIterator to be advanced.
654 @return A Pointer to the next jsonObject within the jsonObject being traversed; or NULL.
656 If the jsonObject being traversed is of type JSON_HASH, jsonIteratorNext returns a pointer
657 to the next jsonObject within the internal osrfHash. The associated key string is available
658 via the pointer member itr->key.
660 If the jsonObject being traversed is of type JSON_ARRAY, jsonIteratorNext returns a pointer
661 to the next jsonObject within the internal osrfList.
663 In either case, the jsonIterator remains at the same level within the jsonObject that it is
664 traversing. It does @em not descend to traverse deeper levels recursively.
666 If there is no next jsonObject within the jsonObject being traversed, jsonIteratorNext
667 returns NULL. It also returns NULL if @a itr is NULL, or if the jsonIterator is
668 detectably corrupted, or if the jsonObject to be traversed is of a type other than
669 JSON_HASH or JSON_ARRAY.
671 Once jsonIteratorNext has reached the end of the jsonObject that it is traversing,
672 subsequent calls using the same iterator will continue to return NULL. There is no available
673 function to start over at the beginning.
675 The pointer returned, if not NULL, points to an internal element of the jsonObject being
676 traversed. The calling code should @em not try to free it, but it may modify its contents.
678 jsonObject* jsonIteratorNext(jsonIterator* itr) {
679 if(!(itr && itr->obj)) return NULL;
680 if( itr->obj->type == JSON_HASH ) {
684 jsonObject* item = osrfHashIteratorNext(itr->hashItr);
686 itr->key = osrfHashIteratorKey(itr->hashItr);
691 return jsonObjectGetIndex( itr->obj, itr->index++ );
696 @brief Determine whether a jsonIterator is positioned at the last element of a jsonObject.
697 @param itr Pointer to the jsonIterator whose position is to be tested.
698 @return An int, as boolean: 0 if the iterator is positioned at the end, or 1 if it isn't.
700 If the jsonIterator is positioned where a call to jsonIteratorNext() would return NULL,
701 then jsonIteratorHasNext returns 0. Otherwise it returns 1.
703 int jsonIteratorHasNext(const jsonIterator* itr) {
704 if(!(itr && itr->obj)) return 0;
705 if( itr->obj->type == JSON_HASH )
706 return osrfHashIteratorHasNext( itr->hashItr );
707 return (itr->index < itr->obj->size) ? 1 : 0;
711 @brief Fetch a pointer to a specified element within a jsonObject of type JSON_ARRAY.
712 @param obj Pointer to the outer jsonObject.
713 @param index A zero-based index identifying the element to be fetched.
714 @return A pointer to the element at the specified location, if any, or NULL.
716 The return value is NULL if @a obj is null, or if the outer jsonObject is not of type
717 JSON_ARRAY, or if there is no element at the specified location.
719 If not NULL, the pointer returned points to an element within the outer jsonObject. The
720 calling code should @em not try to free it, but it may modify its contents.
722 jsonObject* jsonObjectGetIndex( const jsonObject* obj, unsigned long index ) {
723 if(!obj) return NULL;
724 return (obj->type == JSON_ARRAY) ?
725 (OSRF_LIST_GET_INDEX(obj->value.l, index)) : NULL;
729 @brief Remove a specified element from a jsonObject of type JSON_ARRAY.
730 @param dest Pointer to the jsonObject from which the element is to be removed.
731 @param index A zero-based index identifying the element to be removed.
732 @return The number of elements remaining at the top level, or -1 upon error.
734 The return value is -1 if @a dest is NULL, or if it points to a jsonObject not of type
735 JSON_ARRAY. Otherwise it reflects the number of elements remaining in the top level of
736 the outer jsonObject, not counting any at lower levels.
738 unsigned long jsonObjectRemoveIndex(jsonObject* dest, unsigned long index) {
739 if( dest && dest->type == JSON_ARRAY ) {
740 osrfListRemove(dest->value.l, index);
741 return dest->value.l->size;
747 @brief Extract a specified element from a jsonObject of type JSON_ARRAY.
748 @param dest Pointer to the jsonObject from which the element is to be extracted.
749 @param index A zero-based index identifying the element to be extracted.
750 @return A pointer to the extracted element, if successful; otherwise NULL.
752 THe return value is NULL if @a dest is NULL, or if it points to a jsonObject not of type
753 JSON_ARRAY, or if there is no element at the specified location.
755 Otherwise, the calling code assumes ownership of the jsonObject to which the return value
756 points, and is responsible for freeing it by calling jsonObjectFree(). The original outer
757 jsonObject remains unchanged except for the removal of the specified element.
759 This function is sijmilar to jsonObjectRemoveIndex(), except that it returns a pointer to
760 the removed sub-object instead of destroying it.
762 jsonObject* jsonObjectExtractIndex(jsonObject* dest, unsigned long index) {
763 if( dest && dest->type == JSON_ARRAY ) {
764 jsonObject* obj = osrfListExtract(dest->value.l, index);
773 @brief Remove an element, specified by key, from a jsonObject of type JSON_HASH.
774 @param dest Pointer to the outer jsonObject from which an element is to be removed.
775 @param key The key for the associated element to be removed.
776 @return 1 if successful, or -1 if not.
778 The operation is considered successful if @a dest and @a key are both non-NULL, and
779 @a dest points to a jsonObject of type JSON_HASH, even if the specified key is not found.
781 unsigned long jsonObjectRemoveKey( jsonObject* dest, const char* key) {
782 if( dest && key && dest->type == JSON_HASH ) {
783 osrfHashRemove(dest->value.h, key);
790 @brief Format a double into a character string.
791 @param num The double to be formatted.
792 @return A newly allocated character string containing the formatted number.
794 The number is formatted according to the printf-style format specification "%.30g". and
795 will therefore contain no more than 30 significant digits.
797 The calling code is responsible for freeing the resulting string.
799 char* doubleToString( double num ) {
802 size_t len = snprintf(buf, sizeof( buf ), "%.30g", num) + 1;
803 if( len < sizeof( buf ) )
804 return strdup( buf );
807 // Need a bigger buffer (should never be necessary)
809 char* bigger_buff = safe_malloc( len + 1 );
810 (void) snprintf(bigger_buff, len + 1, "%.30g", num);
816 @brief Fetch a pointer to the string stored in a jsonObject, if any.
817 @param obj Pointer to the jsonObject.
818 @return Pointer to the string stored internally, or NULL.
820 If @a obj points to a jsonObject of type JSON_STRING or JSON_NUMBER, the returned value
821 points to the string stored internally (a numeric string in the case of a JSON_NUMBER).
822 Otherwise the returned value is NULL.
824 const char* jsonObjectGetString(const jsonObject* obj) {
827 if( obj->type == JSON_STRING )
829 else if( obj->type == JSON_NUMBER )
830 return obj->value.s ? obj->value.s : "0";
839 @brief Translate a jsonObject to a double.
840 @param @obj Pointer to the jsonObject.
841 @return The numeric value stored in the jsonObject.
843 If @a obj is NULL, or if it points to a jsonObject not of type JSON_NUMBER, the value
846 double jsonObjectGetNumber( const jsonObject* obj ) {
847 return (obj && obj->type == JSON_NUMBER && obj->value.s)
848 ? strtod( obj->value.s, NULL ) : 0;
852 @brief Store a copy of a specified character string in a jsonObject of type JSON_STRING.
853 @param dest Pointer to the jsonObject in which the string will be stored.
854 @param string Pointer to the string to be stored.
856 Both @a dest and @a string must be non-NULL.
858 If the jsonObject is not already of type JSON_STRING, it is converted to a JSON_STRING,
859 with any previous contents freed.
861 void jsonObjectSetString(jsonObject* dest, const char* string) {
862 if(!(dest && string)) return;
863 JSON_INIT_CLEAR(dest, JSON_STRING);
864 dest->value.s = strdup(string);
868 Turn a jsonObject into a JSON_NUMBER (if it isn't already one) and store
869 a specified numeric string in it. If the string is not numeric,
870 store the equivalent of zero, and return an error status.
873 @brief Store a copy of a numeric character string in a jsonObject of type JSON_NUMBER.
874 @param dest Pointer to the jsonObject in which the number will be stored.
875 @param string Pointer to the numeric string to be stored.
877 Both @a dest and @a string must be non-NULL.
879 If the jsonObject is not already of type JSON_NUMBER, it is converted to a JSON_STRING,
880 with any previous contents freed.
882 If the input string is not numeric as determined by jsonIsNumeric(), the number stored
885 int jsonObjectSetNumberString(jsonObject* dest, const char* string) {
886 if(!(dest && string)) return -1;
887 JSON_INIT_CLEAR(dest, JSON_NUMBER);
889 if( jsonIsNumeric( string ) ) {
890 dest->value.s = strdup(string);
894 dest->value.s = NULL; // equivalent to zero
900 @brief Store a number in a jsonObject of type JSON_NUMBER.
901 @param dest Pointer to the jsonObject in which the number will be stored.
902 @param num The number to be stored.
904 If the jsonObject is not already of type JSON_NUMBER, it is converted to one, with any
905 previous contents freed.
907 void jsonObjectSetNumber(jsonObject* dest, double num) {
909 JSON_INIT_CLEAR(dest, JSON_NUMBER);
910 dest->value.s = doubleToString( num );
914 @brief Assign a class name to a jsonObject.
915 @param dest Pointer to the jsonObject.
916 @param classname Pointer to a string containing the class name.
918 Both dest and classname must be non-NULL.
920 void jsonObjectSetClass(jsonObject* dest, const char* classname ) {
921 if(!(dest && classname)) return;
922 free(dest->classname);
923 dest->classname = strdup(classname);
927 @brief Fetch a pointer to the class name of a jsonObject, if any.
928 @param dest Pointer to the jsonObject.
929 @return Pointer to a string containing the class name, if there is one; or NULL.
931 If not NULL, the pointer returned points to a string stored internally within the
932 jsonObject. The calling code should @em not try to free it.
934 const char* jsonObjectGetClass(const jsonObject* dest) {
935 if(!dest) return NULL;
936 return dest->classname;
940 @brief Create a copy of an existing jsonObject, including all internal sub-objects.
941 @param o Pointer to the jsonObject to be copied.
942 @return A pointer to the newly created copy.
944 The calling code is responsible for freeing the copy of the original.
946 jsonObject* jsonObjectClone( const jsonObject* o ) {
947 if(!o) return jsonNewObject(NULL);
954 jsonObject* result = NULL;
958 result = jsonNewObject(NULL);
961 result = jsonNewObject(jsonObjectGetString(o));
964 result = jsonNewObject( o->value.s );
965 result->type = JSON_NUMBER;
968 result = jsonNewBoolObject(jsonBoolIsTrue((jsonObject*) o));
971 arr = jsonNewObject(NULL);
972 arr->type = JSON_ARRAY;
973 for(i=0; i < o->size; i++)
974 jsonObjectPush(arr, jsonObjectClone(jsonObjectGetIndex(o, i)));
978 hash = jsonNewObject(NULL);
979 hash->type = JSON_HASH;
980 itr = jsonNewIterator(o);
981 while( (tmp = jsonIteratorNext(itr)) )
982 jsonObjectSetKey(hash, itr->key, jsonObjectClone(tmp));
983 jsonIteratorFree(itr);
988 jsonObjectSetClass(result, jsonObjectGetClass(o));
993 @brief Return the truth or falsity of a jsonObject of type JSON_BOOL.
994 @param boolObj Pointer to the jsonObject.
995 @return 1 or 0, depending on whether the stored boolean is true or false.
997 If @a boolObj is NULL, or if it points to a jsonObject not of type JSON_BOOL, the
998 returned value is zero.
1000 int jsonBoolIsTrue( const jsonObject* boolObj ) {
1001 if( boolObj && boolObj->type == JSON_BOOL && boolObj->value.b )
1008 @brief Create a copy of the string stored in a jsonObject.
1009 @param o Pointer to the jsonObject whose string is to be copied.
1010 @return Pointer to a newly allocated string copied from the jsonObject.
1012 If @a o is NULL, or if it points to a jsonObject not of type JSON_STRING or JSON_NUMBER,
1013 the returned value is NULL. In the case of a JSON_NUMBER, the string created is numeric.
1015 The calling code is responsible for freeing the newly created string.
1017 char* jsonObjectToSimpleString( const jsonObject* o ) {
1025 value = strdup( o->value.s ? o->value.s : "0" );
1029 value = strdup(o->value.s);
1036 Return 1 if the string is numeric, otherwise return 0.
1037 This validation follows the rules defined by the grammar at:
1038 http://www.json.org/
1041 @brief Determine whether a specified character string is a valid JSON number.
1042 @param s Pointer to the string to be examined.
1043 @return 1 if the string is numeric, or 0 if not.
1045 This function defines numericity according to JSON rules; see http://json.org/. This
1046 determination is based purely on the lexical properties of the string. In particular
1047 there is no guarantee that the number in a numeric string is representable in C as a
1048 long long, a long double, or any other built-in type.
1050 A numeric string consists of:
1052 - An optional leading minus sign (but not a plus sign)
1053 - One or more decimal digits. The first digit may be a zero only if it is the only
1054 digit to the left of the decimal.
1055 - Optionally, a decimal point followed by one or more decimal digits.
1056 - An optional exponent, consisting of:
1057 - The letter E, in upper or lower case
1058 - An optional plus or minus sign
1059 - One or more decimal digits
1061 See also jsonScrubNumber().
1063 int jsonIsNumeric( const char* s ) {
1065 if( !s || !*s ) return 0;
1069 // skip leading minus sign, if present (leading plus sign not allowed)
1074 // There must be at least one digit to the left of the decimal
1076 if( isdigit( (unsigned char) *p ) ) {
1079 // If the first digit is zero, it must be the
1080 // only digit to the left of the decimal
1082 if( isdigit( (unsigned char) *p ) )
1087 // Skip over the following digits
1089 while( isdigit( (unsigned char) *p ) ) ++p;
1096 return 1; // integer
1102 // If there is a decimal point, there must be
1103 // at least one digit to the right of it
1105 if( isdigit( (unsigned char) *p ) )
1110 // skip over contiguous digits
1112 while( isdigit( (unsigned char) *p ) ) ++p;
1116 return 1; // decimal fraction, no exponent
1117 else if( *p != 'e' && *p != 'E' )
1118 return 0; // extra junk, no exponent
1122 // If we get this far, we have the beginnings of an exponent.
1123 // Skip over optional sign of exponent.
1125 if( '-' == *p || '+' == *p )
1128 // There must be at least one digit in the exponent
1130 if( isdigit( (unsigned char) *p ) )
1135 // skip over contiguous digits
1137 while( isdigit( (unsigned char) *p ) ) ++p;
1140 return 0; // extra junk
1142 return 1; // number with exponent
1146 @brief Edit a string into a valid JSON number, if possible.
1147 @param s Pointer to the string to be edited.
1148 @return A pointer to a newly created numeric string, if possible; otherwise NULL.
1150 JSON has rather exacting requirements about what constitutes a valid numeric string (see
1151 jsonIsNumeric()). Real-world input may be a bit sloppy. jsonScrubNumber accepts numeric
1152 strings in a less formal format and reformats them, where possible, according to JSON
1153 rules. It removes leading white space, a leading plus sign, and extraneous leading zeros.
1154 It adds a leading zero as needed when the absolute value is less than 1. It also accepts
1155 scientific notation in the form of a bare exponent (e.g. "E-3"), supplying a leading factor
1158 If the input string is non-numeric even according to these relaxed rules, the return value
1161 The calling code is responsible for freeing the newly created numeric string.
1163 char* jsonScrubNumber( const char* s ) {
1164 if( !s || !*s ) return NULL;
1166 growing_buffer* buf = buffer_init( 64 );
1168 // Skip leading white space, if present
1170 while( isspace( (unsigned char) *s ) ) ++s;
1172 // Skip leading plus sign, if present, but keep a minus
1176 buffer_add_char( buf, '-' );
1179 else if( '+' == *s )
1188 // Skip any leading zeros
1190 while( '0' == *s ) ++s;
1192 // Capture digits to the left of the decimal,
1193 // and note whether there are any.
1195 int left_digit = 0; // boolean
1197 if( isdigit( (unsigned char) *s ) ) {
1198 buffer_add_char( buf, *s++ );
1202 while( isdigit( (unsigned char) *s ) )
1203 buffer_add_char( buf, *s++ );
1205 // Now we expect to see a decimal point,
1206 // an exponent, or end-of-string.
1214 // Add a single leading zero, if we need to
1217 buffer_add_char( buf, '0' );
1218 buffer_add_char( buf, '.' );
1221 if( ! left_digit && ! isdigit( (unsigned char) *s ) )
1223 // No digits on either side of decimal
1229 // Collect digits to right of decimal
1231 while( isdigit( (unsigned char) *s ) )
1232 buffer_add_char( buf, *s++ );
1239 // Exponent; we'll deal with it later, but
1240 // meanwhile make sure we have something
1244 buffer_add_char( buf, '1' );
1248 // Unexpected character; bail out
1254 if( '\0' == *s ) // Are we done yet?
1255 return buffer_release( buf );
1257 if( 'e' != *s && 'E' != *s ) {
1259 // Unexpected character: bail out
1265 // We have an exponent. Load the e or E,
1266 // and the sign if there is one.
1268 buffer_add_char( buf, *s++ );
1270 if( '+' == *s || '-' == *s )
1271 buffer_add_char( buf, *s++ );
1273 // Collect digits of the exponent
1275 while( isdigit( (unsigned char) *s ) )
1276 buffer_add_char( buf, *s++ );
1278 // There better not be anything left
1285 return buffer_release( buf );