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>
24 /* cleans up an object if it is morphing another object, also
25 * verifies that the appropriate storage container exists where appropriate */
26 #define JSON_INIT_CLEAR(_obj_, newtype) \
27 if( _obj_->type == JSON_HASH && newtype != JSON_HASH ) { \
28 osrfHashFree(_obj_->value.h); \
29 _obj_->value.h = NULL; \
30 } else if( _obj_->type == JSON_ARRAY && newtype != JSON_ARRAY ) { \
31 osrfListFree(_obj_->value.l); \
32 _obj_->value.l = NULL; \
33 } else if( _obj_->type == JSON_STRING || _obj_->type == JSON_NUMBER ) { \
34 free(_obj_->value.s); \
35 _obj_->value.s = NULL; \
37 _obj_->type = newtype;\
38 if( newtype == JSON_HASH && _obj_->value.h == NULL ) { \
39 _obj_->value.h = osrfNewHash(); \
40 osrfHashSetCallback( _obj_->value.h, _jsonFreeHashItem ); \
41 } else if( newtype == JSON_ARRAY && _obj_->value.l == NULL ) { \
42 _obj_->value.l = osrfNewList(); \
43 _obj_->value.l->freeItem = _jsonFreeListItem;\
46 static int unusedObjCapture = 0;
47 static int unusedObjRelease = 0;
48 static int mallocObjCreate = 0;
49 static int currentListLen = 0;
53 union unusedObjUnion* next;
56 typedef union unusedObjUnion unusedObj;
58 // We maintain a free list of jsonObjects that are available
59 // for use, in order to reduce the churning through
60 // malloc() and free().
62 static unusedObj* freeObjList = NULL;
64 static void add_json_to_buffer( const jsonObject* obj, growing_buffer * buf );
67 * Return all unused jsonObjects to the heap
70 void jsonObjectFreeUnused( void ) {
73 while( freeObjList ) {
74 temp = freeObjList->next;
80 jsonObject* jsonNewObject(const char* data) {
85 o = (jsonObject*) freeObjList;
86 freeObjList = freeObjList->next;
90 OSRF_MALLOC( o, sizeof(jsonObject) );
99 o->type = JSON_STRING;
100 o->value.s = strdup(data);
109 jsonObject* jsonNewObjectFmt(const char* data, ...) {
114 o = (jsonObject*) freeObjList;
115 freeObjList = freeObjList->next;
119 OSRF_MALLOC( o, sizeof(jsonObject) );
128 VA_LIST_TO_STRING(data);
129 o->type = JSON_STRING;
130 o->value.s = strdup(VA_BUF);
140 jsonObject* jsonNewNumberObject( double num ) {
141 jsonObject* o = jsonNewObject(NULL);
142 o->type = JSON_NUMBER;
143 o->value.s = doubleToString( num );
148 * Creates a new number object from a numeric string
150 jsonObject* jsonNewNumberStringObject( const char* numstr ) {
153 else if( !jsonIsNumeric( numstr ) )
156 jsonObject* o = jsonNewObject(NULL);
157 o->type = JSON_NUMBER;
158 o->value.s = strdup( numstr );
162 jsonObject* jsonNewBoolObject(int val) {
163 jsonObject* o = jsonNewObject(NULL);
169 jsonObject* jsonNewObjectType(int type) {
170 jsonObject* o = jsonNewObject(NULL);
175 void jsonObjectFree( jsonObject* o ) {
177 if(!o || o->parent) return;
181 case JSON_HASH : osrfHashFree(o->value.h); break;
182 case JSON_ARRAY : osrfListFree(o->value.l); break;
183 case JSON_STRING : free(o->value.s); break;
184 case JSON_NUMBER : free(o->value.s); break;
187 // Stick the old jsonObject onto a free list
188 // for potential reuse
190 unusedObj* unused = (unusedObj*) o;
191 unused->next = freeObjList;
192 freeObjList = unused;
196 if (unusedObjCapture > 1 && !(unusedObjCapture % 1000))
197 osrfLogDebug( OSRF_LOG_MARK, "Objects malloc()'d: %d, Reusable objects captured: %d, Objects reused: %d, Current List Length: %d", mallocObjCreate, unusedObjCapture, unusedObjRelease, currentListLen );
200 static void _jsonFreeHashItem(char* key, void* item){
202 jsonObject* o = (jsonObject*) item;
203 o->parent = NULL; /* detach the item */
206 static void _jsonFreeListItem(void* item){
208 jsonObject* o = (jsonObject*) item;
209 o->parent = NULL; /* detach the item */
213 void jsonSetBool(jsonObject* bl, int val) {
215 JSON_INIT_CLEAR(bl, JSON_BOOL);
219 unsigned long jsonObjectPush(jsonObject* o, jsonObject* newo) {
221 if(!newo) newo = jsonNewObject(NULL);
222 JSON_INIT_CLEAR(o, JSON_ARRAY);
224 osrfListPush( o->value.l, newo );
225 o->size = o->value.l->size;
229 unsigned long jsonObjectSetIndex(jsonObject* dest, unsigned long index, jsonObject* newObj) {
231 if(!newObj) newObj = jsonNewObject(NULL);
232 JSON_INIT_CLEAR(dest, JSON_ARRAY);
233 newObj->parent = dest;
234 osrfListSet( dest->value.l, newObj, index );
235 dest->size = dest->value.l->size;
236 return dest->value.l->size;
239 unsigned long jsonObjectSetKey( jsonObject* o, const char* key, jsonObject* newo) {
241 if(!newo) newo = jsonNewObject(NULL);
242 JSON_INIT_CLEAR(o, JSON_HASH);
244 osrfHashSet( o->value.h, newo, key );
245 o->size = osrfHashGetCount(o->value.h);
249 jsonObject* jsonObjectGetKey( jsonObject* obj, const char* key ) {
250 if(!(obj && obj->type == JSON_HASH && obj->value.h && key)) return NULL;
251 return osrfHashGet( obj->value.h, key);
254 const jsonObject* jsonObjectGetKeyConst( const jsonObject* obj, const char* key ) {
255 if(!(obj && obj->type == JSON_HASH && obj->value.h && key)) return NULL;
256 return osrfHashGet( obj->value.h, key);
259 char* jsonObjectToJSON( const jsonObject* obj ) {
260 jsonObject* obj2 = jsonObjectEncodeClass( obj );
261 char* json = jsonObjectToJSONRaw(obj2);
262 jsonObjectFree(obj2);
266 char* jsonObjectToJSONRaw( const jsonObject* obj ) {
267 if(!obj) return NULL;
268 growing_buffer* buf = buffer_init(32);
269 add_json_to_buffer( obj, buf );
270 return buffer_release( buf );
273 static void add_json_to_buffer( const jsonObject* obj, growing_buffer * buf ) {
278 if(obj->value.b) OSRF_BUFFER_ADD(buf, "true");
279 else OSRF_BUFFER_ADD(buf, "false");
283 if(obj->value.s) OSRF_BUFFER_ADD( buf, obj->value.s );
284 else OSRF_BUFFER_ADD_CHAR( buf, '0' );
289 OSRF_BUFFER_ADD(buf, "null");
293 OSRF_BUFFER_ADD_CHAR(buf, '"');
294 char* data = obj->value.s;
295 int len = strlen(data);
297 char* output = uescape(data, len, 1);
298 OSRF_BUFFER_ADD(buf, output);
300 OSRF_BUFFER_ADD_CHAR(buf, '"');
304 OSRF_BUFFER_ADD_CHAR(buf, '[');
307 for( i = 0; i != obj->value.l->size; i++ ) {
308 if(i > 0) OSRF_BUFFER_ADD(buf, ",");
309 add_json_to_buffer( OSRF_LIST_GET_INDEX(obj->value.l, i), buf );
312 OSRF_BUFFER_ADD_CHAR(buf, ']');
318 OSRF_BUFFER_ADD_CHAR(buf, '{');
319 osrfHashIterator* itr = osrfNewHashIterator(obj->value.h);
323 while( (item = osrfHashIteratorNext(itr)) ) {
324 if(i++ > 0) OSRF_BUFFER_ADD(buf, ",");
325 buffer_fadd(buf, "\"%s\":", osrfHashIteratorKey(itr));
326 add_json_to_buffer( item, buf );
329 osrfHashIteratorFree(itr);
330 OSRF_BUFFER_ADD_CHAR(buf, '}');
337 jsonIterator* jsonNewIterator(const jsonObject* obj) {
338 if(!obj) return NULL;
340 OSRF_MALLOC(itr, sizeof(jsonIterator));
342 itr->obj = (jsonObject*) obj;
346 if( obj->type == JSON_HASH )
347 itr->hashItr = osrfNewHashIterator(obj->value.h);
352 void jsonIteratorFree(jsonIterator* itr) {
355 osrfHashIteratorFree(itr->hashItr);
359 jsonObject* jsonIteratorNext(jsonIterator* itr) {
360 if(!(itr && itr->obj)) return NULL;
361 if( itr->obj->type == JSON_HASH ) {
362 if(!itr->hashItr) return NULL;
363 jsonObject* item = osrfHashIteratorNext(itr->hashItr);
364 if(!item) return NULL;
366 itr->key = strdup( osrfHashIteratorKey(itr->hashItr) );
369 return jsonObjectGetIndex( itr->obj, itr->index++ );
373 int jsonIteratorHasNext(const jsonIterator* itr) {
374 if(!(itr && itr->obj)) return 0;
375 if( itr->obj->type == JSON_HASH )
376 return osrfHashIteratorHasNext( itr->hashItr );
377 return (itr->index < itr->obj->size) ? 1 : 0;
380 jsonObject* jsonObjectGetIndex( const jsonObject* obj, unsigned long index ) {
381 if(!obj) return NULL;
382 return (obj->type == JSON_ARRAY) ?
383 (OSRF_LIST_GET_INDEX(obj->value.l, index)) : NULL;
388 unsigned long jsonObjectRemoveIndex(jsonObject* dest, unsigned long index) {
389 if( dest && dest->type == JSON_ARRAY ) {
390 osrfListRemove(dest->value.l, index);
391 return dest->value.l->size;
397 unsigned long jsonObjectRemoveKey( jsonObject* dest, const char* key) {
398 if( dest && key && dest->type == JSON_HASH ) {
399 osrfHashRemove(dest->value.h, key);
406 Allocate a buffer and format a specified numeric value into it.
407 Caller is responsible for freeing the buffer.
409 char* doubleToString( double num ) {
412 size_t len = snprintf(buf, sizeof( buf ), "%.30g", num) + 1;
413 if( len < sizeof( buf ) )
414 return strdup( buf );
417 // Need a bigger buffer (should never be necessary)
419 char* bigger_buff = safe_malloc( len + 1 );
420 (void) snprintf(bigger_buff, len + 1, "%.30g", num);
425 char* jsonObjectGetString(const jsonObject* obj) {
428 if( obj->type == JSON_STRING )
430 else if( obj->type == JSON_NUMBER )
431 return obj->value.s ? obj->value.s : "0";
439 double jsonObjectGetNumber( const jsonObject* obj ) {
440 return (obj && obj->type == JSON_NUMBER && obj->value.s)
441 ? strtod( obj->value.s, NULL ) : 0;
444 void jsonObjectSetString(jsonObject* dest, const char* string) {
445 if(!(dest && string)) return;
446 JSON_INIT_CLEAR(dest, JSON_STRING);
447 dest->value.s = strdup(string);
451 Turn a jsonObject into a JSON_NUMBER (if it isn't already one) and store
452 a specified numeric string in it. If the string is not numeric,
453 store the equivalent of zero, and return an error status.
455 int jsonObjectSetNumberString(jsonObject* dest, const char* string) {
456 if(!(dest && string)) return -1;
457 JSON_INIT_CLEAR(dest, JSON_NUMBER);
459 if( jsonIsNumeric( string ) ) {
460 dest->value.s = strdup(string);
464 dest->value.s = NULL; // equivalent to zero
469 void jsonObjectSetNumber(jsonObject* dest, double num) {
471 JSON_INIT_CLEAR(dest, JSON_NUMBER);
472 dest->value.s = doubleToString( num );
475 void jsonObjectSetClass(jsonObject* dest, const char* classname ) {
476 if(!(dest && classname)) return;
477 free(dest->classname);
478 dest->classname = strdup(classname);
480 const char* jsonObjectGetClass(const jsonObject* dest) {
481 if(!dest) return NULL;
482 return dest->classname;
485 jsonObject* jsonObjectClone( const jsonObject* o ) {
486 if(!o) return jsonNewObject(NULL);
493 jsonObject* result = NULL;
497 result = jsonNewObject(NULL);
500 result = jsonNewObject(jsonObjectGetString(o));
503 result = jsonNewObject( o->value.s );
504 result->type = JSON_NUMBER;
507 result = jsonNewBoolObject(jsonBoolIsTrue((jsonObject*) o));
510 arr = jsonNewObject(NULL);
511 arr->type = JSON_ARRAY;
512 for(i=0; i < o->size; i++)
513 jsonObjectPush(arr, jsonObjectClone(jsonObjectGetIndex(o, i)));
517 hash = jsonNewObject(NULL);
518 hash->type = JSON_HASH;
519 itr = jsonNewIterator(o);
520 while( (tmp = jsonIteratorNext(itr)) )
521 jsonObjectSetKey(hash, itr->key, jsonObjectClone(tmp));
522 jsonIteratorFree(itr);
527 jsonObjectSetClass(result, jsonObjectGetClass(o));
531 int jsonBoolIsTrue( const jsonObject* boolObj ) {
532 if( boolObj && boolObj->type == JSON_BOOL && boolObj->value.b )
538 char* jsonObjectToSimpleString( const jsonObject* o ) {
546 value = strdup( o->value.s ? o->value.s : "0" );
550 value = strdup(o->value.s);
557 Return 1 if the string is numeric, otherwise return 0.
558 This validation follows the rules defined by the grammar at:
561 int jsonIsNumeric( const char* s ) {
563 if( !s || !*s ) return 0;
567 // skip leading minus sign, if present (leading plus sign not allowed)
572 // There must be at least one digit to the left of the decimal
574 if( isdigit( (unsigned char) *p ) ) {
577 // If the first digit is zero, it must be the
578 // only digit to the lerft of the decimal
580 if( isdigit( (unsigned char) *p ) )
585 // Skip oer the following digits
587 while( isdigit( (unsigned char) *p ) ) ++p;
600 // If there is a decimal point, there must be
601 // at least one digit to the right of it
603 if( isdigit( (unsigned char) *p ) )
608 // skip over contiguous digits
610 while( isdigit( (unsigned char) *p ) ) ++p;
614 return 1; // decimal fraction, no exponent
615 else if( *p != 'e' && *p != 'E' )
616 return 0; // extra junk, no exponent
620 // If we get this far, we have the beginnings of an exponent.
621 // Skip over optional sign of exponent.
623 if( '-' == *p || '+' == *p )
626 // There must be at least one digit in the exponent
628 if( isdigit( (unsigned char) *p ) )
633 // skip over contiguous digits
635 while( isdigit( (unsigned char) *p ) ) ++p;
638 return 0; // extra junk
640 return 1; // number with exponent
644 Allocate and reformat a numeric string into one that is valid
645 by JSON rules. If the string is not numeric, return NULL.
646 Caller is responsible for freeing the buffer.
648 char* jsonScrubNumber( const char* s ) {
649 if( !s || !*s ) return NULL;
651 growing_buffer* buf = buffer_init( 64 );
653 // Skip leading white space, if present
655 while( isspace( (unsigned char) *s ) ) ++s;
657 // Skip leading plus sign, if present, but keep a minus
661 buffer_add_char( buf, '-' );
673 // Skip any leading zeros
675 while( '0' == *s ) ++s;
677 // Capture digits to the left of the decimal,
678 // and note whether there are any.
680 int left_digit = 0; // boolean
682 if( isdigit( (unsigned char) *s ) ) {
683 buffer_add_char( buf, *s++ );
687 while( isdigit( (unsigned char) *s ) )
688 buffer_add_char( buf, *s++ );
690 // Now we expect to see a decimal point,
691 // an exponent, or end-of-string.
699 // Add a single leading zero, if we need to
702 buffer_add_char( buf, '0' );
703 buffer_add_char( buf, '.' );
706 if( ! left_digit && ! isdigit( (unsigned char) *s ) )
708 // No digits on either side of decimal
714 // Collect digits to right of decimal
716 while( isdigit( (unsigned char) *s ) )
717 buffer_add_char( buf, *s++ );
724 // Exponent; we'll deal with it later, but
725 // meanwhile make sure we have something
729 buffer_add_char( buf, '1' );
733 // Unexpected character; bail out
739 if( '\0' == *s ) // Are we done yet?
740 return buffer_release( buf );
742 if( 'e' != *s && 'E' != *s ) {
744 // Unexpected character: bail out
750 // We have an exponent. Load the e or E,
751 // and the sign if there is one.
753 buffer_add_char( buf, *s++ );
755 if( '+' == *s || '-' == *s )
756 buffer_add_char( buf, *s++ );
758 // Collect digits of the exponent
760 while( isdigit( (unsigned char) *s ) )
761 buffer_add_char( buf, *s++ );
763 // There better not be anything left
770 return buffer_release( buf );