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.
20 #include <opensrf/log.h>
21 #include <opensrf/osrf_json.h>
22 #include <opensrf/osrf_json_utils.h>
23 #include <opensrf/osrf_utf8.h>
25 /* cleans up an object if it is morphing another object, also
26 * verifies that the appropriate storage container exists where appropriate */
27 #define JSON_INIT_CLEAR(_obj_, newtype) \
28 if( _obj_->type == JSON_HASH && newtype != JSON_HASH ) { \
29 osrfHashFree(_obj_->value.h); \
30 _obj_->value.h = NULL; \
31 } else if( _obj_->type == JSON_ARRAY && newtype != JSON_ARRAY ) { \
32 osrfListFree(_obj_->value.l); \
33 _obj_->value.l = NULL; \
34 } else if( _obj_->type == JSON_STRING || _obj_->type == JSON_NUMBER ) { \
35 free(_obj_->value.s); \
36 _obj_->value.s = NULL; \
37 } else if( _obj_->type == JSON_BOOL && newtype != JSON_BOOL ) { \
38 _obj_->value.l = NULL; \
40 _obj_->type = newtype; \
41 if( newtype == JSON_HASH && _obj_->value.h == NULL ) { \
42 _obj_->value.h = osrfNewHash(); \
43 osrfHashSetCallback( _obj_->value.h, _jsonFreeHashItem ); \
44 } else if( newtype == JSON_ARRAY && _obj_->value.l == NULL ) { \
45 _obj_->value.l = osrfNewList(); \
46 _obj_->value.l->freeItem = _jsonFreeListItem;\
49 static int unusedObjCapture = 0;
50 static int unusedObjRelease = 0;
51 static int mallocObjCreate = 0;
52 static int currentListLen = 0;
56 union unusedObjUnion* next;
59 typedef union unusedObjUnion unusedObj;
61 // We maintain a free list of jsonObjects that are available
62 // for use, in order to reduce the churning through
63 // malloc() and free().
65 static unusedObj* freeObjList = NULL;
67 static void add_json_to_buffer( const jsonObject* obj,
68 growing_buffer * buf, int do_classname, int second_pass );
71 * Return all unused jsonObjects to the heap
74 void jsonObjectFreeUnused( void ) {
77 while( freeObjList ) {
78 temp = freeObjList->next;
84 jsonObject* jsonNewObject(const char* data) {
89 o = (jsonObject*) freeObjList;
90 freeObjList = freeObjList->next;
94 OSRF_MALLOC( o, sizeof(jsonObject) );
103 o->type = JSON_STRING;
104 o->value.s = strdup(data);
113 jsonObject* jsonNewObjectFmt(const char* data, ...) {
118 o = (jsonObject*) freeObjList;
119 freeObjList = freeObjList->next;
123 OSRF_MALLOC( o, sizeof(jsonObject) );
132 VA_LIST_TO_STRING(data);
133 o->type = JSON_STRING;
134 o->value.s = strdup(VA_BUF);
144 jsonObject* jsonNewNumberObject( double num ) {
145 jsonObject* o = jsonNewObject(NULL);
146 o->type = JSON_NUMBER;
147 o->value.s = doubleToString( num );
152 * Creates a new number object from a numeric string
154 jsonObject* jsonNewNumberStringObject( const char* numstr ) {
157 else if( !jsonIsNumeric( numstr ) )
160 jsonObject* o = jsonNewObject(NULL);
161 o->type = JSON_NUMBER;
162 o->value.s = strdup( numstr );
166 jsonObject* jsonNewBoolObject(int val) {
167 jsonObject* o = jsonNewObject(NULL);
173 jsonObject* jsonNewObjectType(int type) {
174 jsonObject* o = jsonNewObject(NULL);
179 void jsonObjectFree( jsonObject* o ) {
181 if(!o || o->parent) return;
185 case JSON_HASH : osrfHashFree(o->value.h); break;
186 case JSON_ARRAY : osrfListFree(o->value.l); break;
187 case JSON_STRING : free(o->value.s); break;
188 case JSON_NUMBER : free(o->value.s); break;
191 // Stick the old jsonObject onto a free list
192 // for potential reuse
194 unusedObj* unused = (unusedObj*) o;
195 unused->next = freeObjList;
196 freeObjList = unused;
200 if (unusedObjCapture > 1 && !(unusedObjCapture % 1000))
201 osrfLogDebug( OSRF_LOG_MARK, "Objects malloc()'d: %d, "
202 "Reusable objects captured: %d, Objects reused: %d, "
203 "Current List Length: %d",
204 mallocObjCreate, unusedObjCapture, unusedObjRelease, currentListLen );
207 static void _jsonFreeHashItem(char* key, void* item){
209 jsonObject* o = (jsonObject*) item;
210 o->parent = NULL; /* detach the item */
213 static void _jsonFreeListItem(void* item){
215 jsonObject* o = (jsonObject*) item;
216 o->parent = NULL; /* detach the item */
220 void jsonSetBool(jsonObject* bl, int val) {
222 JSON_INIT_CLEAR(bl, JSON_BOOL);
226 unsigned long jsonObjectPush(jsonObject* o, jsonObject* newo) {
228 if(!newo) newo = jsonNewObject(NULL);
229 JSON_INIT_CLEAR(o, JSON_ARRAY);
231 osrfListPush( o->value.l, newo );
232 o->size = o->value.l->size;
236 unsigned long jsonObjectSetIndex(jsonObject* dest, unsigned long index, jsonObject* newObj) {
238 if(!newObj) newObj = jsonNewObject(NULL);
239 JSON_INIT_CLEAR(dest, JSON_ARRAY);
240 newObj->parent = dest;
241 osrfListSet( dest->value.l, newObj, index );
242 dest->size = dest->value.l->size;
243 return dest->value.l->size;
246 unsigned long jsonObjectSetKey( jsonObject* o, const char* key, jsonObject* newo) {
248 if(!newo) newo = jsonNewObject(NULL);
249 JSON_INIT_CLEAR(o, JSON_HASH);
251 osrfHashSet( o->value.h, newo, key );
252 o->size = osrfHashGetCount(o->value.h);
256 jsonObject* jsonObjectGetKey( jsonObject* obj, const char* key ) {
257 if(!(obj && obj->type == JSON_HASH && obj->value.h && key)) return NULL;
258 return osrfHashGet( obj->value.h, key);
261 const jsonObject* jsonObjectGetKeyConst( const jsonObject* obj, const char* key ) {
262 if(!(obj && obj->type == JSON_HASH && obj->value.h && key)) return NULL;
263 return osrfHashGet( obj->value.h, key);
267 * Recursively traverse a jsonObject, formatting it into a JSON string.
269 * The last two parameters are booleans.
271 * If do_classname is true, examine each node for a classname, and if you
272 * find one, pretend that the node is under an extra layer of JSON_HASH, with
273 * JSON_CLASS_KEY and JSON_DATA_KEY as keys.
275 * second_pass should always be false except for some recursive calls. It
276 * is used when expanding classnames, to distinguish between the first and
277 * second passes through a given node.
281 static void add_json_to_buffer( const jsonObject* obj,
282 growing_buffer * buf, int do_classname, int second_pass ) {
285 OSRF_BUFFER_ADD(buf, "null");
289 if( obj->classname && do_classname )
295 // Pretend we see an extra layer of JSON_HASH
297 OSRF_BUFFER_ADD( buf, "{\"" );
298 OSRF_BUFFER_ADD( buf, JSON_CLASS_KEY );
299 OSRF_BUFFER_ADD( buf, "\":\"" );
300 OSRF_BUFFER_ADD( buf, obj->classname );
301 OSRF_BUFFER_ADD( buf, "\",\"" );
302 OSRF_BUFFER_ADD( buf, JSON_DATA_KEY );
303 OSRF_BUFFER_ADD( buf, "\":" );
304 add_json_to_buffer( obj, buf, 1, 1 );
305 buffer_add_char( buf, '}' );
313 if(obj->value.b) OSRF_BUFFER_ADD(buf, "true");
314 else OSRF_BUFFER_ADD(buf, "false");
318 if(obj->value.s) OSRF_BUFFER_ADD( buf, obj->value.s );
319 else OSRF_BUFFER_ADD_CHAR( buf, '0' );
324 OSRF_BUFFER_ADD(buf, "null");
328 OSRF_BUFFER_ADD_CHAR(buf, '"');
329 buffer_append_utf8(buf, obj->value.s);
330 OSRF_BUFFER_ADD_CHAR(buf, '"');
334 OSRF_BUFFER_ADD_CHAR(buf, '[');
337 for( i = 0; i != obj->value.l->size; i++ ) {
338 if(i > 0) OSRF_BUFFER_ADD(buf, ",");
340 OSRF_LIST_GET_INDEX(obj->value.l, i), buf, do_classname, second_pass );
343 OSRF_BUFFER_ADD_CHAR(buf, ']');
349 OSRF_BUFFER_ADD_CHAR(buf, '{');
350 osrfHashIterator* itr = osrfNewHashIterator(obj->value.h);
354 while( (item = osrfHashIteratorNext(itr)) ) {
355 if(i++ > 0) OSRF_BUFFER_ADD_CHAR(buf, ',');
356 OSRF_BUFFER_ADD_CHAR(buf, '"');
357 buffer_append_utf8(buf, osrfHashIteratorKey(itr));
358 OSRF_BUFFER_ADD(buf, "\":");
359 add_json_to_buffer( item, buf, do_classname, second_pass );
362 osrfHashIteratorFree(itr);
363 OSRF_BUFFER_ADD_CHAR(buf, '}');
369 char* jsonObjectToJSONRaw( const jsonObject* obj ) {
370 if(!obj) return NULL;
371 growing_buffer* buf = buffer_init(32);
372 add_json_to_buffer( obj, buf, 0, 0 );
373 return buffer_release( buf );
376 char* jsonObjectToJSON( const jsonObject* obj ) {
377 if(!obj) return NULL;
378 growing_buffer* buf = buffer_init(32);
379 add_json_to_buffer( obj, buf, 1, 0 );
380 return buffer_release( buf );
383 jsonIterator* jsonNewIterator(const jsonObject* obj) {
384 if(!obj) return NULL;
386 OSRF_MALLOC(itr, sizeof(jsonIterator));
388 itr->obj = (jsonObject*) obj;
392 if( obj->type == JSON_HASH )
393 itr->hashItr = osrfNewHashIterator(obj->value.h);
400 void jsonIteratorFree(jsonIterator* itr) {
402 osrfHashIteratorFree(itr->hashItr);
406 jsonObject* jsonIteratorNext(jsonIterator* itr) {
407 if(!(itr && itr->obj)) return NULL;
408 if( itr->obj->type == JSON_HASH ) {
412 jsonObject* item = osrfHashIteratorNext(itr->hashItr);
414 itr->key = osrfHashIteratorKey(itr->hashItr);
419 return jsonObjectGetIndex( itr->obj, itr->index++ );
423 int jsonIteratorHasNext(const jsonIterator* itr) {
424 if(!(itr && itr->obj)) return 0;
425 if( itr->obj->type == JSON_HASH )
426 return osrfHashIteratorHasNext( itr->hashItr );
427 return (itr->index < itr->obj->size) ? 1 : 0;
430 jsonObject* jsonObjectGetIndex( const jsonObject* obj, unsigned long index ) {
431 if(!obj) return NULL;
432 return (obj->type == JSON_ARRAY) ?
433 (OSRF_LIST_GET_INDEX(obj->value.l, index)) : NULL;
438 unsigned long jsonObjectRemoveIndex(jsonObject* dest, unsigned long index) {
439 if( dest && dest->type == JSON_ARRAY ) {
440 osrfListRemove(dest->value.l, index);
441 return dest->value.l->size;
447 jsonObject* jsonObjectExtractIndex(jsonObject* dest, unsigned long index) {
448 if( dest && dest->type == JSON_ARRAY ) {
449 return osrfListExtract(dest->value.l, index);
455 unsigned long jsonObjectRemoveKey( jsonObject* dest, const char* key) {
456 if( dest && key && dest->type == JSON_HASH ) {
457 osrfHashRemove(dest->value.h, key);
464 Allocate a buffer and format a specified numeric value into it.
465 Caller is responsible for freeing the buffer.
467 char* doubleToString( double num ) {
470 size_t len = snprintf(buf, sizeof( buf ), "%.30g", num) + 1;
471 if( len < sizeof( buf ) )
472 return strdup( buf );
475 // Need a bigger buffer (should never be necessary)
477 char* bigger_buff = safe_malloc( len + 1 );
478 (void) snprintf(bigger_buff, len + 1, "%.30g", num);
483 char* jsonObjectGetString(const jsonObject* obj) {
486 if( obj->type == JSON_STRING )
488 else if( obj->type == JSON_NUMBER )
489 return obj->value.s ? obj->value.s : "0";
497 double jsonObjectGetNumber( const jsonObject* obj ) {
498 return (obj && obj->type == JSON_NUMBER && obj->value.s)
499 ? strtod( obj->value.s, NULL ) : 0;
502 void jsonObjectSetString(jsonObject* dest, const char* string) {
503 if(!(dest && string)) return;
504 JSON_INIT_CLEAR(dest, JSON_STRING);
505 dest->value.s = strdup(string);
509 Turn a jsonObject into a JSON_NUMBER (if it isn't already one) and store
510 a specified numeric string in it. If the string is not numeric,
511 store the equivalent of zero, and return an error status.
513 int jsonObjectSetNumberString(jsonObject* dest, const char* string) {
514 if(!(dest && string)) return -1;
515 JSON_INIT_CLEAR(dest, JSON_NUMBER);
517 if( jsonIsNumeric( string ) ) {
518 dest->value.s = strdup(string);
522 dest->value.s = NULL; // equivalent to zero
527 void jsonObjectSetNumber(jsonObject* dest, double num) {
529 JSON_INIT_CLEAR(dest, JSON_NUMBER);
530 dest->value.s = doubleToString( num );
533 void jsonObjectSetClass(jsonObject* dest, const char* classname ) {
534 if(!(dest && classname)) return;
535 free(dest->classname);
536 dest->classname = strdup(classname);
538 const char* jsonObjectGetClass(const jsonObject* dest) {
539 if(!dest) return NULL;
540 return dest->classname;
543 jsonObject* jsonObjectClone( const jsonObject* o ) {
544 if(!o) return jsonNewObject(NULL);
551 jsonObject* result = NULL;
555 result = jsonNewObject(NULL);
558 result = jsonNewObject(jsonObjectGetString(o));
561 result = jsonNewObject( o->value.s );
562 result->type = JSON_NUMBER;
565 result = jsonNewBoolObject(jsonBoolIsTrue((jsonObject*) o));
568 arr = jsonNewObject(NULL);
569 arr->type = JSON_ARRAY;
570 for(i=0; i < o->size; i++)
571 jsonObjectPush(arr, jsonObjectClone(jsonObjectGetIndex(o, i)));
575 hash = jsonNewObject(NULL);
576 hash->type = JSON_HASH;
577 itr = jsonNewIterator(o);
578 while( (tmp = jsonIteratorNext(itr)) )
579 jsonObjectSetKey(hash, itr->key, jsonObjectClone(tmp));
580 jsonIteratorFree(itr);
585 jsonObjectSetClass(result, jsonObjectGetClass(o));
589 int jsonBoolIsTrue( const jsonObject* boolObj ) {
590 if( boolObj && boolObj->type == JSON_BOOL && boolObj->value.b )
596 char* jsonObjectToSimpleString( const jsonObject* o ) {
604 value = strdup( o->value.s ? o->value.s : "0" );
608 value = strdup(o->value.s);
615 Return 1 if the string is numeric, otherwise return 0.
616 This validation follows the rules defined by the grammar at:
619 int jsonIsNumeric( const char* s ) {
621 if( !s || !*s ) return 0;
625 // skip leading minus sign, if present (leading plus sign not allowed)
630 // There must be at least one digit to the left of the decimal
632 if( isdigit( (unsigned char) *p ) ) {
635 // If the first digit is zero, it must be the
636 // only digit to the left of the decimal
638 if( isdigit( (unsigned char) *p ) )
643 // Skip over the following digits
645 while( isdigit( (unsigned char) *p ) ) ++p;
658 // If there is a decimal point, there must be
659 // at least one digit to the right of it
661 if( isdigit( (unsigned char) *p ) )
666 // skip over contiguous digits
668 while( isdigit( (unsigned char) *p ) ) ++p;
672 return 1; // decimal fraction, no exponent
673 else if( *p != 'e' && *p != 'E' )
674 return 0; // extra junk, no exponent
678 // If we get this far, we have the beginnings of an exponent.
679 // Skip over optional sign of exponent.
681 if( '-' == *p || '+' == *p )
684 // There must be at least one digit in the exponent
686 if( isdigit( (unsigned char) *p ) )
691 // skip over contiguous digits
693 while( isdigit( (unsigned char) *p ) ) ++p;
696 return 0; // extra junk
698 return 1; // number with exponent
702 Allocate and reformat a numeric string into one that is valid
703 by JSON rules. If the string is not numeric, return NULL.
704 Caller is responsible for freeing the buffer.
706 char* jsonScrubNumber( const char* s ) {
707 if( !s || !*s ) return NULL;
709 growing_buffer* buf = buffer_init( 64 );
711 // Skip leading white space, if present
713 while( isspace( (unsigned char) *s ) ) ++s;
715 // Skip leading plus sign, if present, but keep a minus
719 buffer_add_char( buf, '-' );
731 // Skip any leading zeros
733 while( '0' == *s ) ++s;
735 // Capture digits to the left of the decimal,
736 // and note whether there are any.
738 int left_digit = 0; // boolean
740 if( isdigit( (unsigned char) *s ) ) {
741 buffer_add_char( buf, *s++ );
745 while( isdigit( (unsigned char) *s ) )
746 buffer_add_char( buf, *s++ );
748 // Now we expect to see a decimal point,
749 // an exponent, or end-of-string.
757 // Add a single leading zero, if we need to
760 buffer_add_char( buf, '0' );
761 buffer_add_char( buf, '.' );
764 if( ! left_digit && ! isdigit( (unsigned char) *s ) )
766 // No digits on either side of decimal
772 // Collect digits to right of decimal
774 while( isdigit( (unsigned char) *s ) )
775 buffer_add_char( buf, *s++ );
782 // Exponent; we'll deal with it later, but
783 // meanwhile make sure we have something
787 buffer_add_char( buf, '1' );
791 // Unexpected character; bail out
797 if( '\0' == *s ) // Are we done yet?
798 return buffer_release( buf );
800 if( 'e' != *s && 'E' != *s ) {
802 // Unexpected character: bail out
808 // We have an exponent. Load the e or E,
809 // and the sign if there is one.
811 buffer_add_char( buf, *s++ );
813 if( '+' == *s || '-' == *s )
814 buffer_add_char( buf, *s++ );
816 // Collect digits of the exponent
818 while( isdigit( (unsigned char) *s ) )
819 buffer_add_char( buf, *s++ );
821 // There better not be anything left
828 return buffer_release( buf );