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_utf8.h>
35 /* cleans up an object if it is morphing another object, also
36 * verifies that the appropriate storage container exists where appropriate */
38 @brief Coerce a jsonObj into a specified type, if it isn't already of that type.
39 @param _obj_ Pointer to the jsonObject to be coerced.
40 @param newtype The desired type.
42 If the old type and the new type don't match, discard and free the old contents.
44 If the old type is JSON_STRING or JSON_NUMBER, free the internal string buffer even
45 if the type is not changing.
47 If the new type is JSON_ARRAY or JSON_HASH, make sure there is an osrfList or osrfHash
48 in the jsonObject, respectively.
50 #define JSON_INIT_CLEAR(_obj_, newtype) \
51 if( _obj_->type == JSON_HASH && newtype != JSON_HASH ) { \
52 osrfHashFree(_obj_->value.h); \
53 _obj_->value.h = NULL; \
54 } else if( _obj_->type == JSON_ARRAY && newtype != JSON_ARRAY ) { \
55 osrfListFree(_obj_->value.l); \
56 _obj_->value.l = NULL; \
57 } else if( _obj_->type == JSON_STRING || _obj_->type == JSON_NUMBER ) { \
58 free(_obj_->value.s); \
59 _obj_->value.s = NULL; \
60 } else if( _obj_->type == JSON_BOOL && newtype != JSON_BOOL ) { \
61 _obj_->value.l = NULL; \
63 _obj_->type = newtype; \
64 if( newtype == JSON_HASH && _obj_->value.h == NULL ) { \
65 _obj_->value.h = osrfNewHash(); \
66 osrfHashSetCallback( _obj_->value.h, _jsonFreeHashItem ); \
67 } else if( newtype == JSON_ARRAY && _obj_->value.l == NULL ) { \
68 _obj_->value.l = osrfNewList(); \
69 _obj_->value.l->freeItem = _jsonFreeListItem;\
72 /** Count of the times we put a freed jsonObject on the free list instead of calling free() */
73 static int unusedObjCapture = 0;
74 /** Count of the times we reused a jsonObject from the free list instead of calling malloc() */
75 static int unusedObjRelease = 0;
76 /** Count of the times we allocated a jsonObject with malloc() */
77 static int mallocObjCreate = 0;
78 /** Number of unused jsonObjects currently on the free list */
79 static int currentListLen = 0;
82 Union overlaying a jsonObject with a pointer. When the jsonObject is not in use as a
83 jsonObject, we use the overlaid pointer to maintain a linked list of unused jsonObjects.
87 union unusedObjUnion* next;
90 typedef union unusedObjUnion unusedObj;
92 /** Pointer to the head of the free list */
93 static unusedObj* freeObjList = NULL;
95 static void add_json_to_buffer( const jsonObject* obj,
96 growing_buffer * buf, int do_classname, int second_pass );
99 @brief Return all jsonObjects in the free list to the heap.
101 Reclaims memory occupied by unused jsonObjects in the free list. It is never really
102 necessary to call this function, assuming that we don't run out of memory. However
103 it might be worth calling if we have built and destroyed a lot of jsonObjects that
104 we don't expect to need again, in order to reduce our memory footprint.
106 void jsonObjectFreeUnused( void ) {
109 while( freeObjList ) {
110 temp = freeObjList->next;
117 @brief Create a new jsonObject, optionally containing a string.
118 @param data Pointer to a string to be stored in the jsonObject; may be NULL.
119 @return Pointer to a newly allocate jsonObject.
121 If @a data is NULL, create a jsonObject of type JSON_NULL. Otherwise create
122 a jsonObject of type JSON_STRING, containing the specified string.
124 The calling code is responsible for freeing the jsonObject by calling jsonObjectFree().
126 jsonObject* jsonNewObject(const char* data) {
130 // Allocate a jsonObject; from the free list if possible,
131 // or from the heap if necessary.
133 o = (jsonObject*) freeObjList;
134 freeObjList = freeObjList->next;
138 OSRF_MALLOC( o, sizeof(jsonObject) );
147 o->type = JSON_STRING;
148 o->value.s = strdup(data);
158 @brief Create a jsonObject, optionally containing a formatted string.
159 @param data Pointer to a printf-style format string; may be NULL. Subsequent parameters,
160 if any, will be formatted and inserted into the resulting string.
161 @return Pointer to a newly created jsonObject.
163 If @a data is NULL, create a jsonObject of type JSON_NULL, and ignore any extra parameters.
164 Otherwise create a jsonObject of type JSON_STRING, containing the formatted string.
166 The calling code is responsible for freeing the jsonObject by calling jsonObjectFree().
168 jsonObject* jsonNewObjectFmt(const char* data, ...) {
173 o = (jsonObject*) freeObjList;
174 freeObjList = freeObjList->next;
178 OSRF_MALLOC( o, sizeof(jsonObject) );
187 VA_LIST_TO_STRING(data);
188 o->type = JSON_STRING;
189 o->value.s = strdup(VA_BUF);
200 @brief Create a new jsonObject of type JSON_NUMBER.
201 @param num The number to store in the jsonObject.
202 @return Pointer to the newly created jsonObject.
204 The number is stored internally as a character string, as formatted by
207 The calling code is responsible for freeing the jsonObject by calling jsonObjectFree().
209 jsonObject* jsonNewNumberObject( double num ) {
210 jsonObject* o = jsonNewObject(NULL);
211 o->type = JSON_NUMBER;
212 o->value.s = doubleToString( num );
217 @brief Create a new jsonObject of type JSON_NUMBER from a numeric string.
218 @param numstr Pointer to a numeric character string.
219 @return Pointer to a newly created jsonObject containing the numeric value, or NULL
220 if the string is not numeric.
222 The jsonIsNumeric() function determines whether the input string is numeric.
224 The calling code is responsible for freeing the jsonObject by calling jsonObjectFree().
226 jsonObject* jsonNewNumberStringObject( const char* numstr ) {
229 else if( !jsonIsNumeric( numstr ) )
232 jsonObject* o = jsonNewObject(NULL);
233 o->type = JSON_NUMBER;
234 o->value.s = strdup( numstr );
239 @brief Create a new jsonObject of type JSON_BOOL, with a specified boolean value.
240 @param val An int used as a boolean value.
241 @return Pointer to the new jsonObject.
243 Zero represents false, and non-zero represents true, according to the usual convention.
244 In practice the value of @a val is stored unchanged, but the calling code should not try to
245 take advantage of that fact, because future versions may behave differently.
247 The calling code is responsible for freeing the jsonObject by calling jsonObjectFree().
249 jsonObject* jsonNewBoolObject(int val) {
250 jsonObject* o = jsonNewObject(NULL);
257 @brief Create a new jsonObject of a specified type, with a default value.
258 @param type One of the 6 JSON types, as specified by the JSON_* macros.
259 @return Pointer to the new jsonObject.
261 An invalid type parameter will go unnoticed, and may lead to unpleasantness.
263 The default value is equivalent to an empty string (for a JSON_STRING), zero (for a
264 JSON_NUMBER), an empty hash (for a JSON_HASH), an empty array (for a JSON_ARRAY), or
265 false (for a JSON_BOOL). A JSON_NULL, of course, has no value, but can still be
268 The calling code is responsible for freeing the jsonObject by calling jsonObjectFree().
270 jsonObject* jsonNewObjectType(int type) {
271 jsonObject* o = jsonNewObject(NULL);
273 if( JSON_BOOL == type )
279 @brief Free a jsonObject and everything in it.
280 @param o Pointer to the jsonObject to be freed.
282 Any jsonObjects stored inside the jsonObject (in hashes or arrays) will be freed as
283 well, and so one, recursively.
285 void jsonObjectFree( jsonObject* o ) {
287 if(!o || o->parent) return;
291 case JSON_HASH : osrfHashFree(o->value.h); break;
292 case JSON_ARRAY : osrfListFree(o->value.l); break;
293 case JSON_STRING : free(o->value.s); break;
294 case JSON_NUMBER : free(o->value.s); break;
297 // Stick the old jsonObject onto a free list
298 // for potential reuse
300 unusedObj* unused = (unusedObj*) o;
301 unused->next = freeObjList;
302 freeObjList = unused;
306 if (unusedObjCapture > 1 && !(unusedObjCapture % 1000))
307 osrfLogDebug( OSRF_LOG_MARK, "Objects malloc()'d: %d, "
308 "Reusable objects captured: %d, Objects reused: %d, "
309 "Current List Length: %d",
310 mallocObjCreate, unusedObjCapture, unusedObjRelease, currentListLen );
314 @brief Free a jsonObject through a void pointer.
316 @param item Pointer to the jsonObject to be freed, cast to a void pointer.
318 This function is a callback for freeing jsonObjects stored in an osrfHash.
320 static void _jsonFreeHashItem(char* key, void* item){
322 jsonObject* o = (jsonObject*) item;
323 o->parent = NULL; /* detach the item */
328 @brief Free a jsonObject through a void pointer.
329 @param item Pointer to the jsonObject to be freed, cast to a void pointer.
331 This function is a callback for freeing jsonObjects stored in an osrfList.
333 static void _jsonFreeListItem(void* item){
335 jsonObject* o = (jsonObject*) item;
336 o->parent = NULL; /* detach the item */
341 @brief Assign a boolean value to a jsonObject of type JSON_BOOL.
342 @param bl Pointer to the jsonObject.
343 @param val The boolean value to be applied, encoded as an int.
345 If the jsonObject is not already of type JSON_BOOL, it is converted to one, and
346 any previous contents are freed.
348 Zero represents false, and non-zero represents true, according to the usual convention.
349 In practice the value of @a val is stored unchanged, but the calling code should not try to
350 take advantage of that fact, because future versions may behave differently.
352 void jsonSetBool(jsonObject* bl, int val) {
354 JSON_INIT_CLEAR(bl, JSON_BOOL);
359 @brief Insert one jsonObject into a jsonObect of type JSON_ARRAY, at the end of the array.
360 @param o Pointer to the outer jsonObject that will receive additional payload.
361 @param newo Pointer to the inner jsonObject, or NULL.
362 @return The number of jsonObjects directly subordinate to the outer jsonObject,
365 If the pointer to the outer jsonObject is NULL, jsonObjectPush returns -1.
367 If the outer jsonObject is not already of type JSON_ARRAY, it is converted to one, and
368 any previous contents are freed.
370 If the pointer to the inner jsonObject is NULL, jsonObjectPush creates a new jsonObject
371 of type JSON_NULL, and appends it to the array.
373 unsigned long jsonObjectPush(jsonObject* o, jsonObject* newo) {
375 if(!newo) newo = jsonNewObject(NULL);
376 JSON_INIT_CLEAR(o, JSON_ARRAY);
378 osrfListPush( o->value.l, newo );
379 o->size = o->value.l->size;
384 @brief Insert a jsonObject at a specified position in a jsonObject of type JSON_ARRAY.
385 @param dest Pointer to the outer jsonObject that will receive the new payload.
386 @param index A zero-based subscript specifying the position of the new jsonObject.
387 @param newObj Pointer to the new jsonObject to be inserted, or NULL.
388 @return The size of the internal osrfList where the array is stored, or -1 on error.
390 If @a dest is NULL, jsonObjectSetIndex returns -1.
392 If the outer jsonObject is not already of type JSON_ARRAY, it is converted to one, and
393 any previous contents are freed.
395 If @a newObj is NULL, jsonObject creates a new jsonObject of type JSON_NULL and
396 inserts it into the array.
398 If there is already a jsonObject at the specified location, it is freed and replaced.
400 Depending on the placement of the inner jsonObject, it may leave unoccupied holes in the
401 array that are included in the reported size. As a result of this and other peculiarities
402 of the underlying osrfList within the jsonObject, the reported size may not reflect the
403 number of jsonObjects in the array. See osrf_list.c for further details.
405 unsigned long jsonObjectSetIndex(jsonObject* dest, unsigned long index, jsonObject* newObj) {
407 if(!newObj) newObj = jsonNewObject(NULL);
408 JSON_INIT_CLEAR(dest, JSON_ARRAY);
409 newObj->parent = dest;
410 osrfListSet( dest->value.l, newObj, index );
411 dest->size = dest->value.l->size;
412 return dest->value.l->size;
416 @brief Insert a jsonObject into a jsonObject of type JSON_HASH, for a specified key.
417 @param o Pointer to the outer jsonObject that will receive the new payload.
418 @param key Pointer to a string that will serve as the key.
419 @param newo Pointer to the new jsonObject that will be inserted, or NULL.
420 @return The number of items stored in the first level of the hash.
422 If the @a o parameter is NULL, jsonObjectSetKey returns -1.
424 If the outer jsonObject is not already of type JSON_HASH, it will be converted to one,
425 and any previous contents will be freed.
427 If @a key is NULL, jsonObjectSetKey returns the size of the hash without doing anything
428 (apart from possibly converting the outer jsonObject as described above).
430 If @a newo is NULL, jsonObjectSetKey creates a new jsonObject of type JSON_NULL and inserts
431 it with the specified key.
433 If a previous jsonObject is already stored with the same key, it is freed and replaced.
435 unsigned long jsonObjectSetKey( jsonObject* o, const char* key, jsonObject* newo) {
437 if(!newo) newo = jsonNewObject(NULL);
438 JSON_INIT_CLEAR(o, JSON_HASH);
440 osrfHashSet( o->value.h, newo, key );
441 o->size = osrfHashGetCount(o->value.h);
446 @brief From a jsonObject of type JSON_HASH, find the inner jsonObject for a specified key.
447 @param obj Pointer to the outer jsonObject.
448 @param key The key for the associated item to be found.
449 @return A pointer to the inner jsonObject, if found, or NULL if not.
451 Returns NULL if either parameter is NULL, or if @a obj points to a jsonObject not of type
452 JSON_HASH, or if the specified key is not found in the outer jsonObject.
454 The returned pointer (if not NULL) points to the interior of the outer jsonObject. The
455 calling code should @em not try to free it, but it may change its contents.
457 jsonObject* jsonObjectGetKey( jsonObject* obj, const char* key ) {
458 if(!(obj && obj->type == JSON_HASH && obj->value.h && key)) return NULL;
459 return osrfHashGet( obj->value.h, key);
463 @brief From a jsonObject of type JSON_HASH, find the inner jsonObject for a specified key.
464 @param obj Pointer to the outer jsonObject.
465 @param key The key for the associated item to be found.
466 @return A pointer to the inner jsonObject, if found, or NULL if not.
468 This function is identical to jsonObjectGetKey(), except that the outer jsonObject may be
469 const, and the pointer returned points to const. As a result, the calling code is put on
470 notice that it should not try to modify the contents of the inner jsonObject.
472 const jsonObject* jsonObjectGetKeyConst( const jsonObject* obj, const char* key ) {
473 if(!(obj && obj->type == JSON_HASH && obj->value.h && key)) return NULL;
474 return osrfHashGet( obj->value.h, key);
478 @brief Recursively traverse a jsonObject, translating it into a JSON string.
479 @param obj Pointer to the jsonObject to be translated.
480 @param buf Pointer to a growing_buffer that will receive the JSON string.
481 @param do_classname Boolean; if true, expand (i.e. encode) class names.
482 @param second_pass Boolean; should always be false except for some recursive calls.
484 If @a do_classname is true, expand any class names, as described in the discussion of
487 @a second_pass should always be false except for some recursive calls. It is used
488 when expanding classnames, to distinguish between the first and second passes
489 through a given node.
491 static void add_json_to_buffer( const jsonObject* obj,
492 growing_buffer * buf, int do_classname, int second_pass ) {
495 OSRF_BUFFER_ADD(buf, "null");
499 if( obj->classname && do_classname )
505 // Pretend we see an extra layer of JSON_HASH
507 OSRF_BUFFER_ADD( buf, "{\"" );
508 OSRF_BUFFER_ADD( buf, JSON_CLASS_KEY );
509 OSRF_BUFFER_ADD( buf, "\":\"" );
510 OSRF_BUFFER_ADD( buf, obj->classname );
511 OSRF_BUFFER_ADD( buf, "\",\"" );
512 OSRF_BUFFER_ADD( buf, JSON_DATA_KEY );
513 OSRF_BUFFER_ADD( buf, "\":" );
514 add_json_to_buffer( obj, buf, 1, 1 );
515 buffer_add_char( buf, '}' );
523 if(obj->value.b) OSRF_BUFFER_ADD(buf, "true");
524 else OSRF_BUFFER_ADD(buf, "false");
528 if(obj->value.s) OSRF_BUFFER_ADD( buf, obj->value.s );
529 else OSRF_BUFFER_ADD_CHAR( buf, '0' );
534 OSRF_BUFFER_ADD(buf, "null");
538 OSRF_BUFFER_ADD_CHAR(buf, '"');
539 buffer_append_utf8(buf, obj->value.s);
540 OSRF_BUFFER_ADD_CHAR(buf, '"');
544 OSRF_BUFFER_ADD_CHAR(buf, '[');
547 for( i = 0; i != obj->value.l->size; i++ ) {
548 if(i > 0) OSRF_BUFFER_ADD(buf, ",");
550 OSRF_LIST_GET_INDEX(obj->value.l, i), buf, do_classname, second_pass );
553 OSRF_BUFFER_ADD_CHAR(buf, ']');
559 OSRF_BUFFER_ADD_CHAR(buf, '{');
560 osrfHashIterator* itr = osrfNewHashIterator(obj->value.h);
564 while( (item = osrfHashIteratorNext(itr)) ) {
565 if(i++ > 0) OSRF_BUFFER_ADD_CHAR(buf, ',');
566 OSRF_BUFFER_ADD_CHAR(buf, '"');
567 buffer_append_utf8(buf, osrfHashIteratorKey(itr));
568 OSRF_BUFFER_ADD(buf, "\":");
569 add_json_to_buffer( item, buf, do_classname, second_pass );
572 osrfHashIteratorFree(itr);
573 OSRF_BUFFER_ADD_CHAR(buf, '}');
580 @brief Translate a jsonObject into a JSON string, without expanding class names.
581 @param obj Pointer to the jsonObject to be translated.
582 @return A pointer to a newly allocated string containing the JSON.
584 The calling code is responsible for freeing the resulting string.
586 char* jsonObjectToJSONRaw( const jsonObject* obj ) {
587 if(!obj) return NULL;
588 growing_buffer* buf = buffer_init(32);
589 add_json_to_buffer( obj, buf, 0, 0 );
590 return buffer_release( buf );
594 @brief Translate a jsonObject into a JSON string, with expansion of class names.
595 @param obj Pointer to the jsonObject to be translated.
596 @return A pointer to a newly allocated string containing the JSON.
598 At every level, any jsonObject containing a class name will be translated as if it were
599 under an extra layer of JSON_HASH, with JSON_CLASS_KEY as the key for the class name and
600 JSON_DATA_KEY as the key for the jsonObject.
602 The calling code is responsible for freeing the resulting string.
604 char* jsonObjectToJSON( const jsonObject* obj ) {
605 if(!obj) return NULL;
606 growing_buffer* buf = buffer_init(32);
607 add_json_to_buffer( obj, buf, 1, 0 );
608 return buffer_release( buf );
612 @brief Create a new jsonIterator for traversing a specified jsonObject.
613 @param obj Pointer to the jsonObject to be traversed.
614 @return A pointer to the newly allocated jsonIterator, or NULL upon error.
616 jsonNewIterator returns NULL if @a obj is NULL.
618 The new jsonIterator does not point to any particular position within the jsonObject to
619 be traversed. The next call to jsonIteratorNext() will position it at the beginning.
621 The calling code is responsible for freeing the jsonIterator by calling jsonIteratorFree().
623 jsonIterator* jsonNewIterator(const jsonObject* obj) {
624 if(!obj) return NULL;
626 OSRF_MALLOC(itr, sizeof(jsonIterator));
628 itr->obj = (jsonObject*) obj;
632 if( obj->type == JSON_HASH )
633 itr->hashItr = osrfNewHashIterator(obj->value.h);
641 @brief Free a jsonIterator and everything in it.
642 @param itr Pointer to the jsonIterator to be freed.
644 void jsonIteratorFree(jsonIterator* itr) {
646 osrfHashIteratorFree(itr->hashItr);
651 @brief Advance a jsonIterator to the next position within a jsonObject.
652 @param itr Pointer to the jsonIterator to be advanced.
653 @return A Pointer to the next jsonObject within the jsonObject being traversed; or NULL.
655 If the jsonObject being traversed is of type JSON_HASH, jsonIteratorNext returns a pointer
656 to the next jsonObject within the internal osrfHash. The associated key string is available
657 via the pointer member itr->key.
659 If the jsonObject being traversed is of type JSON_ARRAY, jsonIteratorNext returns a pointer
660 to the next jsonObject within the internal osrfList.
662 In either case, the jsonIterator remains at the same level within the jsonObject that it is
663 traversing. It does @em not descend to traverse deeper levels recursively.
665 If there is no next jsonObject within the jsonObject being traversed, jsonIteratorNext
666 returns NULL. It also returns NULL if @a itr is NULL, or if the jsonIterator is
667 detectably corrupted, or if the jsonObject to be traversed is of a type other than
668 JSON_HASH or JSON_ARRAY.
670 Once jsonIteratorNext has reached the end of the jsonObject that it is traversing,
671 subsequent calls using the same iterator will continue to return NULL. There is no available
672 function to start over at the beginning.
674 The pointer returned, if not NULL, points to an internal element of the jsonObject being
675 traversed. The calling code should @em not try to free it, but it may modify its contents.
677 jsonObject* jsonIteratorNext(jsonIterator* itr) {
678 if(!(itr && itr->obj)) return NULL;
679 if( itr->obj->type == JSON_HASH ) {
683 jsonObject* item = osrfHashIteratorNext(itr->hashItr);
685 itr->key = osrfHashIteratorKey(itr->hashItr);
690 return jsonObjectGetIndex( itr->obj, itr->index++ );
695 @brief Determine whether a jsonIterator is positioned at the last element of a jsonObject.
696 @param itr Pointer to the jsonIterator whose position is to be tested.
697 @return An int, as boolean: 0 if the iterator is positioned at the end, or 1 if it isn't.
699 If the jsonIterator is positioned where a call to jsonIteratorNext() would return NULL,
700 then jsonIteratorHasNext returns 0. Otherwise it returns 1.
702 int jsonIteratorHasNext(const jsonIterator* itr) {
703 if(!(itr && itr->obj)) return 0;
704 if( itr->obj->type == JSON_HASH )
705 return osrfHashIteratorHasNext( itr->hashItr );
706 return (itr->index < itr->obj->size) ? 1 : 0;
710 @brief Fetch a pointer to a specified element within a jsonObject of type JSON_ARRAY.
711 @param obj Pointer to the outer jsonObject.
712 @param index A zero-based index identifying the element to be fetched.
713 @return A pointer to the element at the specified location, if any, or NULL.
715 The return value is NULL if @a obj is null, or if the outer jsonObject is not of type
716 JSON_ARRAY, or if there is no element at the specified location.
718 If not NULL, the pointer returned points to an element within the outer jsonObject. The
719 calling code should @em not try to free it, but it may modify its contents.
721 jsonObject* jsonObjectGetIndex( const jsonObject* obj, unsigned long index ) {
722 if(!obj) return NULL;
723 return (obj->type == JSON_ARRAY) ?
724 (OSRF_LIST_GET_INDEX(obj->value.l, index)) : NULL;
728 @brief Remove a specified element from a jsonObject of type JSON_ARRAY.
729 @param dest Pointer to the jsonObject from which the element is to be removed.
730 @param index A zero-based index identifying the element to be removed.
731 @return The number of elements remaining at the top level, or -1 upon error.
733 The return value is -1 if @a dest is NULL, or if it points to a jsonObject not of type
734 JSON_ARRAY. Otherwise it reflects the number of elements remaining in the top level of
735 the outer jsonObject, not counting any at lower levels.
737 unsigned long jsonObjectRemoveIndex(jsonObject* dest, unsigned long index) {
738 if( dest && dest->type == JSON_ARRAY ) {
739 osrfListRemove(dest->value.l, index);
740 return dest->value.l->size;
746 @brief Extract a specified element from a jsonObject of type JSON_ARRAY.
747 @param dest Pointer to the jsonObject from which the element is to be extracted.
748 @param index A zero-based index identifying the element to be extracted.
749 @return A pointer to the extracted element, if successful; otherwise NULL.
751 THe return value is NULL if @a dest is NULL, or if it points to a jsonObject not of type
752 JSON_ARRAY, or if there is no element at the specified location.
754 Otherwise, the calling code assumes ownership of the jsonObject to which the return value
755 points, and is responsible for freeing it by calling jsonObjectFree(). The original outer
756 jsonObject remains unchanged except for the removal of the specified element.
758 This function is sijmilar to jsonObjectRemoveIndex(), except that it returns a pointer to
759 the removed sub-object instead of destroying it.
761 jsonObject* jsonObjectExtractIndex(jsonObject* dest, unsigned long index) {
762 if( dest && dest->type == JSON_ARRAY ) {
763 jsonObject* obj = osrfListExtract(dest->value.l, index);
772 @brief Remove an element, specified by key, from a jsonObject of type JSON_HASH.
773 @param dest Pointer to the outer jsonObject from which an element is to be removed.
774 @param key The key for the associated element to be removed.
775 @return 1 if successful, or -1 if not.
777 The operation is considered successful if @a dest and @a key are both non-NULL, and
778 @a dest points to a jsonObject of type JSON_HASH, even if the specified key is not found.
780 unsigned long jsonObjectRemoveKey( jsonObject* dest, const char* key) {
781 if( dest && key && dest->type == JSON_HASH ) {
782 osrfHashRemove(dest->value.h, key);
789 @brief Format a double into a character string.
790 @param num The double to be formatted.
791 @return A newly allocated character string containing the formatted number.
793 The number is formatted according to the printf-style format specification "%.30g". and
794 will therefore contain no more than 30 significant digits.
796 The calling code is responsible for freeing the resulting string.
798 char* doubleToString( double num ) {
801 size_t len = snprintf(buf, sizeof( buf ), "%.30g", num) + 1;
802 if( len < sizeof( buf ) )
803 return strdup( buf );
806 // Need a bigger buffer (should never be necessary)
808 char* bigger_buff = safe_malloc( len + 1 );
809 (void) snprintf(bigger_buff, len + 1, "%.30g", num);
815 @brief Fetch a pointer to the string stored in a jsonObject, if any.
816 @param obj Pointer to the jsonObject.
817 @return Pointer to the string stored internally, or NULL.
819 If @a obj points to a jsonObject of type JSON_STRING or JSON_NUMBER, the returned value
820 points to the string stored internally (a numeric string in the case of a JSON_NUMBER).
821 Otherwise the returned value is NULL.
823 const char* jsonObjectGetString(const jsonObject* obj) {
826 if( obj->type == JSON_STRING )
828 else if( obj->type == JSON_NUMBER )
829 return obj->value.s ? obj->value.s : "0";
838 @brief Translate a jsonObject to a double.
839 @param @obj Pointer to the jsonObject.
840 @return The numeric value stored in the jsonObject.
842 If @a obj is NULL, or if it points to a jsonObject not of type JSON_NUMBER, the value
845 double jsonObjectGetNumber( const jsonObject* obj ) {
846 return (obj && obj->type == JSON_NUMBER && obj->value.s)
847 ? strtod( obj->value.s, NULL ) : 0;
851 @brief Store a copy of a specified character string in a jsonObject of type JSON_STRING.
852 @param dest Pointer to the jsonObject in which the string will be stored.
853 @param string Pointer to the string to be stored.
855 Both @a dest and @a string must be non-NULL.
857 If the jsonObject is not already of type JSON_STRING, it is converted to a JSON_STRING,
858 with any previous contents freed.
860 void jsonObjectSetString(jsonObject* dest, const char* string) {
861 if(!(dest && string)) return;
862 JSON_INIT_CLEAR(dest, JSON_STRING);
863 dest->value.s = strdup(string);
867 Turn a jsonObject into a JSON_NUMBER (if it isn't already one) and store
868 a specified numeric string in it. If the string is not numeric,
869 store the equivalent of zero, and return an error status.
872 @brief Store a copy of a numeric character string in a jsonObject of type JSON_NUMBER.
873 @param dest Pointer to the jsonObject in which the number will be stored.
874 @param string Pointer to the numeric string to be stored.
876 Both @a dest and @a string must be non-NULL.
878 If the jsonObject is not already of type JSON_NUMBER, it is converted to a JSON_STRING,
879 with any previous contents freed.
881 If the input string is not numeric as determined by jsonIsNumeric(), the number stored
884 int jsonObjectSetNumberString(jsonObject* dest, const char* string) {
885 if(!(dest && string)) return -1;
886 JSON_INIT_CLEAR(dest, JSON_NUMBER);
888 if( jsonIsNumeric( string ) ) {
889 dest->value.s = strdup(string);
893 dest->value.s = NULL; // equivalent to zero
899 @brief Store a number in a jsonObject of type JSON_NUMBER.
900 @param dest Pointer to the jsonObject in which the number will be stored.
901 @param num The number to be stored.
903 If the jsonObject is not already of type JSON_NUMBER, it is converted to one, with any
904 previous contents freed.
906 void jsonObjectSetNumber(jsonObject* dest, double num) {
908 JSON_INIT_CLEAR(dest, JSON_NUMBER);
909 dest->value.s = doubleToString( num );
913 @brief Assign a class name to a jsonObject.
914 @param dest Pointer to the jsonObject.
915 @param classname Pointer to a string containing the class name.
917 Both dest and classname must be non-NULL.
919 void jsonObjectSetClass(jsonObject* dest, const char* classname ) {
920 if(!(dest && classname)) return;
921 free(dest->classname);
922 dest->classname = strdup(classname);
926 @brief Fetch a pointer to the class name of a jsonObject, if any.
927 @param dest Pointer to the jsonObject.
928 @return Pointer to a string containing the class name, if there is one; or NULL.
930 If not NULL, the pointer returned points to a string stored internally within the
931 jsonObject. The calling code should @em not try to free it.
933 const char* jsonObjectGetClass(const jsonObject* dest) {
934 if(!dest) return NULL;
935 return dest->classname;
939 @brief Create a copy of an existing jsonObject, including all internal sub-objects.
940 @param o Pointer to the jsonObject to be copied.
941 @return A pointer to the newly created copy.
943 The calling code is responsible for freeing the copy of the original.
945 jsonObject* jsonObjectClone( const jsonObject* o ) {
946 if(!o) return jsonNewObject(NULL);
953 jsonObject* result = NULL;
957 result = jsonNewObject(NULL);
960 result = jsonNewObject(jsonObjectGetString(o));
963 result = jsonNewObject( o->value.s );
964 result->type = JSON_NUMBER;
967 result = jsonNewBoolObject(jsonBoolIsTrue((jsonObject*) o));
970 arr = jsonNewObject(NULL);
971 arr->type = JSON_ARRAY;
972 for(i=0; i < o->size; i++)
973 jsonObjectPush(arr, jsonObjectClone(jsonObjectGetIndex(o, i)));
977 hash = jsonNewObject(NULL);
978 hash->type = JSON_HASH;
979 itr = jsonNewIterator(o);
980 while( (tmp = jsonIteratorNext(itr)) )
981 jsonObjectSetKey(hash, itr->key, jsonObjectClone(tmp));
982 jsonIteratorFree(itr);
987 jsonObjectSetClass(result, jsonObjectGetClass(o));
992 @brief Return the truth or falsity of a jsonObject of type JSON_BOOL.
993 @param boolObj Pointer to the jsonObject.
994 @return 1 or 0, depending on whether the stored boolean is true or false.
996 If @a boolObj is NULL, or if it points to a jsonObject not of type JSON_BOOL, the
997 returned value is zero.
999 int jsonBoolIsTrue( const jsonObject* boolObj ) {
1000 if( boolObj && boolObj->type == JSON_BOOL && boolObj->value.b )
1007 @brief Create a copy of the string stored in a jsonObject.
1008 @param o Pointer to the jsonObject whose string is to be copied.
1009 @return Pointer to a newly allocated string copied from the jsonObject.
1011 If @a o is NULL, or if it points to a jsonObject not of type JSON_STRING or JSON_NUMBER,
1012 the returned value is NULL. In the case of a JSON_NUMBER, the string created is numeric.
1014 The calling code is responsible for freeing the newly created string.
1016 char* jsonObjectToSimpleString( const jsonObject* o ) {
1024 value = strdup( o->value.s ? o->value.s : "0" );
1028 value = strdup(o->value.s);
1035 Return 1 if the string is numeric, otherwise return 0.
1036 This validation follows the rules defined by the grammar at:
1037 http://www.json.org/
1040 @brief Determine whether a specified character string is a valid JSON number.
1041 @param s Pointer to the string to be examined.
1042 @return 1 if the string is numeric, or 0 if not.
1044 This function defines numericity according to JSON rules; see http://json.org/. This
1045 determination is based purely on the lexical properties of the string. In particular
1046 there is no guarantee that the number in a numeric string is representable in C as a
1047 long long, a long double, or any other built-in type.
1049 A numeric string consists of:
1051 - An optional leading minus sign (but not a plus sign)
1052 - One or more decimal digits. The first digit may be a zero only if it is the only
1053 digit to the left of the decimal.
1054 - Optionally, a decimal point followed by one or more decimal digits.
1055 - An optional exponent, consisting of:
1056 - The letter E, in upper or lower case
1057 - An optional plus or minus sign
1058 - One or more decimal digits
1060 See also jsonScrubNumber().
1062 int jsonIsNumeric( const char* s ) {
1064 if( !s || !*s ) return 0;
1068 // skip leading minus sign, if present (leading plus sign not allowed)
1073 // There must be at least one digit to the left of the decimal
1075 if( isdigit( (unsigned char) *p ) ) {
1078 // If the first digit is zero, it must be the
1079 // only digit to the left of the decimal
1081 if( isdigit( (unsigned char) *p ) )
1086 // Skip over the following digits
1088 while( isdigit( (unsigned char) *p ) ) ++p;
1095 return 1; // integer
1101 // If there is a decimal point, there must be
1102 // at least one digit to the right of it
1104 if( isdigit( (unsigned char) *p ) )
1109 // skip over contiguous digits
1111 while( isdigit( (unsigned char) *p ) ) ++p;
1115 return 1; // decimal fraction, no exponent
1116 else if( *p != 'e' && *p != 'E' )
1117 return 0; // extra junk, no exponent
1121 // If we get this far, we have the beginnings of an exponent.
1122 // Skip over optional sign of exponent.
1124 if( '-' == *p || '+' == *p )
1127 // There must be at least one digit in the exponent
1129 if( isdigit( (unsigned char) *p ) )
1134 // skip over contiguous digits
1136 while( isdigit( (unsigned char) *p ) ) ++p;
1139 return 0; // extra junk
1141 return 1; // number with exponent
1145 @brief Edit a string into a valid JSON number, if possible.
1146 @param s Pointer to the string to be edited.
1147 @return A pointer to a newly created numeric string, if possible; otherwise NULL.
1149 JSON has rather exacting requirements about what constitutes a valid numeric string (see
1150 jsonIsNumeric()). Real-world input may be a bit sloppy. jsonScrubNumber accepts numeric
1151 strings in a less formal format and reformats them, where possible, according to JSON
1152 rules. It removes leading white space, a leading plus sign, and extraneous leading zeros.
1153 It adds a leading zero as needed when the absolute value is less than 1. It also accepts
1154 scientific notation in the form of a bare exponent (e.g. "E-3"), supplying a leading factor
1157 If the input string is non-numeric even according to these relaxed rules, the return value
1160 The calling code is responsible for freeing the newly created numeric string.
1162 char* jsonScrubNumber( const char* s ) {
1163 if( !s || !*s ) return NULL;
1165 growing_buffer* buf = buffer_init( 64 );
1167 // Skip leading white space, if present
1169 while( isspace( (unsigned char) *s ) ) ++s;
1171 // Skip leading plus sign, if present, but keep a minus
1175 buffer_add_char( buf, '-' );
1178 else if( '+' == *s )
1187 // Skip any leading zeros
1189 while( '0' == *s ) ++s;
1191 // Capture digits to the left of the decimal,
1192 // and note whether there are any.
1194 int left_digit = 0; // boolean
1196 if( isdigit( (unsigned char) *s ) ) {
1197 buffer_add_char( buf, *s++ );
1201 while( isdigit( (unsigned char) *s ) )
1202 buffer_add_char( buf, *s++ );
1204 // Now we expect to see a decimal point,
1205 // an exponent, or end-of-string.
1213 // Add a single leading zero, if we need to
1216 buffer_add_char( buf, '0' );
1217 buffer_add_char( buf, '.' );
1220 if( ! left_digit && ! isdigit( (unsigned char) *s ) )
1222 // No digits on either side of decimal
1228 // Collect digits to right of decimal
1230 while( isdigit( (unsigned char) *s ) )
1231 buffer_add_char( buf, *s++ );
1238 // Exponent; we'll deal with it later, but
1239 // meanwhile make sure we have something
1243 buffer_add_char( buf, '1' );
1247 // Unexpected character; bail out
1253 if( '\0' == *s ) // Are we done yet?
1254 return buffer_release( buf );
1256 if( 'e' != *s && 'E' != *s ) {
1258 // Unexpected character: bail out
1264 // We have an exponent. Load the e or E,
1265 // and the sign if there is one.
1267 buffer_add_char( buf, *s++ );
1269 if( '+' == *s || '-' == *s )
1270 buffer_add_char( buf, *s++ );
1272 // Collect digits of the exponent
1274 while( isdigit( (unsigned char) *s ) )
1275 buffer_add_char( buf, *s++ );
1277 // There better not be anything left
1284 return buffer_release( buf );