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 returned NULL, subsequent calls using the same iterator will
672 continue to return NULL. There is no available 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 The returned pointer should be treated as a pointer to const. In particular it should
824 @em not be freed. In a future release, the returned pointer may indeed be a pointer
827 char* jsonObjectGetString(const jsonObject* obj) {
830 if( obj->type == JSON_STRING )
832 else if( obj->type == JSON_NUMBER )
833 return obj->value.s ? obj->value.s : "0";
842 @brief Translate a jsonObject to a double.
843 @param @obj Pointer to the jsonObject.
844 @return The numeric value stored in the jsonObject.
846 If @a obj is NULL, or if it points to a jsonObject not of type JSON_NUMBER, the value
849 double jsonObjectGetNumber( const jsonObject* obj ) {
850 return (obj && obj->type == JSON_NUMBER && obj->value.s)
851 ? strtod( obj->value.s, NULL ) : 0;
855 @brief Store a copy of a specified character string in a jsonObject of type JSON_STRING.
856 @param dest Pointer to the jsonObject in which the string will be stored.
857 @param string Pointer to the string to be stored.
859 Both @a dest and @a string must be non-NULL.
861 If the jsonObject is not already of type JSON_STRING, it is converted to a JSON_STRING,
862 with any previous contents freed.
864 void jsonObjectSetString(jsonObject* dest, const char* string) {
865 if(!(dest && string)) return;
866 JSON_INIT_CLEAR(dest, JSON_STRING);
867 dest->value.s = strdup(string);
871 Turn a jsonObject into a JSON_NUMBER (if it isn't already one) and store
872 a specified numeric string in it. If the string is not numeric,
873 store the equivalent of zero, and return an error status.
876 @brief Store a copy of a numeric character string in a jsonObject of type JSON_NUMBER.
877 @param dest Pointer to the jsonObject in which the number will be stored.
878 @param string Pointer to the numeric string to be stored.
880 Both @a dest and @a string must be non-NULL.
882 If the jsonObject is not already of type JSON_NUMBER, it is converted to a JSON_STRING,
883 with any previous contents freed.
885 If the input string is not numeric as determined by jsonIsNumeric(), the number stored
888 int jsonObjectSetNumberString(jsonObject* dest, const char* string) {
889 if(!(dest && string)) return -1;
890 JSON_INIT_CLEAR(dest, JSON_NUMBER);
892 if( jsonIsNumeric( string ) ) {
893 dest->value.s = strdup(string);
897 dest->value.s = NULL; // equivalent to zero
903 @brief Store a number in a jsonObject of type JSON_NUMBER.
904 @param dest Pointer to the jsonObject in which the number will be stored.
905 @param num The number to be stored.
907 If the jsonObject is not already of type JSON_NUMBER, it is converted to one, with any
908 previous contents freed.
910 void jsonObjectSetNumber(jsonObject* dest, double num) {
912 JSON_INIT_CLEAR(dest, JSON_NUMBER);
913 dest->value.s = doubleToString( num );
917 @brief Assign a class name to a jsonObject.
918 @param dest Pointer to the jsonObject.
919 @param classname Pointer to a string containing the class name.
921 Both dest and classname must be non-NULL.
923 void jsonObjectSetClass(jsonObject* dest, const char* classname ) {
924 if(!(dest && classname)) return;
925 free(dest->classname);
926 dest->classname = strdup(classname);
930 @brief Fetch a pointer to the class name of a jsonObject, if any.
931 @param dest Pointer to the jsonObject.
932 @return Pointer to a string containing the class name, if there is one; or NULL.
934 If not NULL, the pointer returned points to a string stored internally within the
935 jsonObject. The calling code should @em not try to free it.
937 const char* jsonObjectGetClass(const jsonObject* dest) {
938 if(!dest) return NULL;
939 return dest->classname;
943 @brief Create a copy of an existing jsonObject, including all internal sub-objects.
944 @param o Pointer to the jsonObject to be copied.
945 @return A pointer to the newly created copy.
947 The calling code is responsible for freeing the copy of the original.
949 jsonObject* jsonObjectClone( const jsonObject* o ) {
950 if(!o) return jsonNewObject(NULL);
957 jsonObject* result = NULL;
961 result = jsonNewObject(NULL);
964 result = jsonNewObject(jsonObjectGetString(o));
967 result = jsonNewObject( o->value.s );
968 result->type = JSON_NUMBER;
971 result = jsonNewBoolObject(jsonBoolIsTrue((jsonObject*) o));
974 arr = jsonNewObject(NULL);
975 arr->type = JSON_ARRAY;
976 for(i=0; i < o->size; i++)
977 jsonObjectPush(arr, jsonObjectClone(jsonObjectGetIndex(o, i)));
981 hash = jsonNewObject(NULL);
982 hash->type = JSON_HASH;
983 itr = jsonNewIterator(o);
984 while( (tmp = jsonIteratorNext(itr)) )
985 jsonObjectSetKey(hash, itr->key, jsonObjectClone(tmp));
986 jsonIteratorFree(itr);
991 jsonObjectSetClass(result, jsonObjectGetClass(o));
996 @brief Return the truth or falsity of a jsonObject of type JSON_BOOL.
997 @param boolObj Pointer to the jsonObject.
998 @return 1 or 0, depending on whether the stored boolean is true or false.
1000 If @a boolObj is NULL, or if it points to a jsonObject not of type JSON_BOOL, the
1001 returned value is zero.
1003 int jsonBoolIsTrue( const jsonObject* boolObj ) {
1004 if( boolObj && boolObj->type == JSON_BOOL && boolObj->value.b )
1011 @brief Create a copy of the string stored in a jsonObject.
1012 @param o Pointer to the jsonObject whose string is to be copied.
1013 @return Pointer to a newly allocated string copied from the jsonObject.
1015 If @a o is NULL, or if it points to a jsonObject not of type JSON_STRING or JSON_NUMBER,
1016 the returned value is NULL. In the case of a JSON_NUMBER, the string created is numeric.
1018 The calling code is responsible for freeing the newly created string.
1020 char* jsonObjectToSimpleString( const jsonObject* o ) {
1028 value = strdup( o->value.s ? o->value.s : "0" );
1032 value = strdup(o->value.s);
1039 Return 1 if the string is numeric, otherwise return 0.
1040 This validation follows the rules defined by the grammar at:
1041 http://www.json.org/
1044 @brief Determine whether a specified character string is a valid JSON number.
1045 @param s Pointer to the string to be examined.
1046 @return 1 if the string is numeric, or 0 if not.
1048 This function defines numericity according to JSON rules; see http://json.org/. This
1049 determination is based purely on the lexical properties of the string. In particular
1050 there is no guarantee that the number in a numeric string is representable in C as a
1051 long long, a long double, or any other built-in type.
1053 A numeric string consists of:
1055 - An optional leading minus sign (but not a plus sign)
1056 - One or more decimal digits. The first digit may be a zero only if it is the only
1057 digit to the left of the decimal.
1058 - Optionally, a decimal point followed by one or more decimal digits.
1059 - An optional exponent, consisting of:
1060 - The letter E, in upper or lower case
1061 - An optional plus or minus sign
1062 - One or more decimal digits
1064 See also jsonScrubNumber().
1066 int jsonIsNumeric( const char* s ) {
1068 if( !s || !*s ) return 0;
1072 // skip leading minus sign, if present (leading plus sign not allowed)
1077 // There must be at least one digit to the left of the decimal
1079 if( isdigit( (unsigned char) *p ) ) {
1082 // If the first digit is zero, it must be the
1083 // only digit to the left of the decimal
1085 if( isdigit( (unsigned char) *p ) )
1090 // Skip over the following digits
1092 while( isdigit( (unsigned char) *p ) ) ++p;
1099 return 1; // integer
1105 // If there is a decimal point, there must be
1106 // at least one digit to the right of it
1108 if( isdigit( (unsigned char) *p ) )
1113 // skip over contiguous digits
1115 while( isdigit( (unsigned char) *p ) ) ++p;
1119 return 1; // decimal fraction, no exponent
1120 else if( *p != 'e' && *p != 'E' )
1121 return 0; // extra junk, no exponent
1125 // If we get this far, we have the beginnings of an exponent.
1126 // Skip over optional sign of exponent.
1128 if( '-' == *p || '+' == *p )
1131 // There must be at least one digit in the exponent
1133 if( isdigit( (unsigned char) *p ) )
1138 // skip over contiguous digits
1140 while( isdigit( (unsigned char) *p ) ) ++p;
1143 return 0; // extra junk
1145 return 1; // number with exponent
1149 @brief Edit a string into a valid JSON number, if possible.
1150 @param s Pointer to the string to be edited.
1151 @return A pointer to a newly created numeric string, if possible; otherwise NULL.
1153 JSON has rather exacting requirements about what constitutes a valid numeric string (see
1154 jsonIsNumeric()). Real-world input may be a bit sloppy. jsonScrubNumber accepts numeric
1155 strings in a less formal format and reformats them, where possible, according to JSON
1156 rules. It removes leading white space, a leading plus sign, and extraneous leading zeros.
1157 It adds a leading zero as needed when the absolute value is less than 1. It also accepts
1158 scientific notation in the form of a bare exponent (e.g. "E-3"), supplying a leading factor
1161 If the input string is non-numeric even according to these relaxed rules, the return value
1164 The calling code is responsible for freeing the newly created numeric string.
1166 char* jsonScrubNumber( const char* s ) {
1167 if( !s || !*s ) return NULL;
1169 growing_buffer* buf = buffer_init( 64 );
1171 // Skip leading white space, if present
1173 while( isspace( (unsigned char) *s ) ) ++s;
1175 // Skip leading plus sign, if present, but keep a minus
1179 buffer_add_char( buf, '-' );
1182 else if( '+' == *s )
1191 // Skip any leading zeros
1193 while( '0' == *s ) ++s;
1195 // Capture digits to the left of the decimal,
1196 // and note whether there are any.
1198 int left_digit = 0; // boolean
1200 if( isdigit( (unsigned char) *s ) ) {
1201 buffer_add_char( buf, *s++ );
1205 while( isdigit( (unsigned char) *s ) )
1206 buffer_add_char( buf, *s++ );
1208 // Now we expect to see a decimal point,
1209 // an exponent, or end-of-string.
1217 // Add a single leading zero, if we need to
1220 buffer_add_char( buf, '0' );
1221 buffer_add_char( buf, '.' );
1224 if( ! left_digit && ! isdigit( (unsigned char) *s ) )
1226 // No digits on either side of decimal
1232 // Collect digits to right of decimal
1234 while( isdigit( (unsigned char) *s ) )
1235 buffer_add_char( buf, *s++ );
1242 // Exponent; we'll deal with it later, but
1243 // meanwhile make sure we have something
1247 buffer_add_char( buf, '1' );
1251 // Unexpected character; bail out
1257 if( '\0' == *s ) // Are we done yet?
1258 return buffer_release( buf );
1260 if( 'e' != *s && 'E' != *s ) {
1262 // Unexpected character: bail out
1268 // We have an exponent. Load the e or E,
1269 // and the sign if there is one.
1271 buffer_add_char( buf, *s++ );
1273 if( '+' == *s || '-' == *s )
1274 buffer_add_char( buf, *s++ );
1276 // Collect digits of the exponent
1278 while( isdigit( (unsigned char) *s ) )
1279 buffer_add_char( buf, *s++ );
1281 // There better not be anything left
1288 return buffer_release( buf );