2 Copyright (C) 2005 Georgia Public Library Service
3 Bill Erickson <highfalutin@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 #include "json_parser.h"
20 /* ---------------------------------------------------------------------- */
21 /* See object.h for function info */
22 /* ---------------------------------------------------------------------- */
25 char* __tabs(int count);
27 jsonObject* jsonNewObject( const char* stringValue ) {
29 jsonObject* obj = (jsonObject*) safe_malloc(sizeof(jsonObject));
31 obj->type = JSON_NULL;
34 obj->type = JSON_STRING;
35 obj->value.s = strdup(stringValue);
44 jsonObject* jsonNewNumberObject( double num ) {
45 jsonObject* o = jsonNewObject(NULL);
46 o->type = JSON_NUMBER;
53 jsonObjectNode* jsonNewObjectNode( jsonObject* obj ) {
54 jsonObjectNode* node = (jsonObjectNode*) safe_malloc(sizeof(jsonObjectNode));
61 unsigned long jsonObjectPush( jsonObject* obj, jsonObject* new_obj) {
64 obj->type = JSON_ARRAY;
67 new_obj = jsonNewObject(NULL);
68 new_obj->type = JSON_NULL;
71 jsonObjectNode* node = jsonNewObjectNode(new_obj);
72 node->index = obj->size++;
74 if(obj->value.c == NULL) {
78 /* append the node onto the end */
79 jsonObjectNode* tmp = obj->value.c;
81 if(tmp->next == NULL) break;
89 unsigned long jsonObjectSetIndex( jsonObject* obj, unsigned long index, jsonObject* new_obj) {
90 if( obj == NULL ) return -1;
91 obj->type = JSON_ARRAY;
93 if(obj->size <= index)
94 obj->size = index + 1;
97 new_obj = jsonNewObject(NULL);
98 new_obj->type = JSON_NULL;
101 jsonObjectNode* node = jsonNewObjectNode(new_obj);
104 if( obj->value.c == NULL ) {
109 if(obj->value.c->index == index) {
110 jsonObjectNode* tmp = obj->value.c->next;
111 jsonObjectNodeFree(obj->value.c);
117 jsonObjectNode* prev = obj->value.c;
118 jsonObjectNode* cur = prev->next;
123 /* replace an existing node */
124 if( cur->index == index ) {
125 jsonObjectNode* tmp = cur->next;
126 jsonObjectNodeFree(cur);
132 /* instert between two nodes */
133 } else if( prev->index < index && cur->index > index ) {
143 /* shove on to the end */
153 void _jsonObjectShifIndex( jsonObject* obj, unsigned long index) {
154 if( obj == NULL || index < 0 ) return;
156 if(obj->value.c == NULL) {
161 jsonObjectNode* data = obj->value.c;
163 if(data->index >= index)
170 unsigned long jsonObjectRemoveIndex( jsonObject* obj, unsigned long index) {
171 if( obj == NULL || index < 0 ) return -1;
173 if(obj->value.c == NULL) return 0;
175 /* removing the first item in the list */
176 if(obj->value.c->index == index) {
177 jsonObjectNode* tmp = obj->value.c->next;
178 jsonObjectNodeFree(obj->value.c);
180 _jsonObjectShiftIndex(obj,index);
185 jsonObjectNode* prev = obj->value.c;
186 jsonObjectNode* cur = prev->next;
189 if(cur->index == index) {
190 jsonObjectNode* tmp = cur->next;
191 jsonObjectNodeFree(cur);
193 _jsonObjectShiftIndex(obj, index);
204 void _jsonObjectShiftIndex(jsonObject* obj, unsigned long index) {
208 if(obj->value.c == NULL) {
213 jsonObjectNode* data = obj->value.c;
215 if(data->index >= index)
223 unsigned long jsonObjectRemoveKey( jsonObject* obj, const char* key) {
224 if( obj == NULL || key == NULL ) return -1;
226 if(obj->value.c == NULL) return 0;
228 /* removing the first item in the list */
229 if(!strcmp(obj->value.c->key, key)) {
231 jsonObjectNode* tmp = obj->value.c->next;
232 jsonObjectNodeFree(obj->value.c);
235 if(!obj->value.c) obj->size = 0;
239 jsonObjectNode* prev = obj->value.c;
240 jsonObjectNode* cur = prev->next;
243 if(!strcmp(cur->key,key)) {
245 jsonObjectNode* tmp = cur->next;
246 jsonObjectNodeFree(cur);
259 unsigned long jsonObjectSetKey( jsonObject* obj, const char* key, jsonObject* new_obj ) {
260 if( obj == NULL || key == NULL ) return -1;
261 obj->type = JSON_HASH;
263 if(new_obj == NULL) {
264 new_obj = jsonNewObject(NULL);
265 new_obj->type = JSON_NULL;
268 jsonObjectNode* node = jsonNewObjectNode(new_obj);
269 node->key = strdup(key);
271 if( obj->value.c == NULL ) {
277 /* replace the first node */
278 if(!strcmp(obj->value.c->key, key)) {
279 jsonObjectNode* tmp = obj->value.c->next;
280 jsonObjectNodeFree(obj->value.c);
286 jsonObjectNode* prev = obj->value.c;
287 jsonObjectNode* cur = prev->next;
292 /* replace an existing node */
293 if( !strcmp(cur->key, key) ) {
294 jsonObjectNode* tmp = cur->next;
295 jsonObjectNodeFree(cur);
306 /* shove on to the end */
318 void jsonObjectFree( jsonObject* obj) {
319 if(obj == NULL) return;
321 free(obj->classname);
324 if( obj->type == JSON_ARRAY || obj->type == JSON_HASH ) {
325 while(obj->value.c) {
326 jsonObjectNode* tmp = obj->value.c->next;
327 jsonObjectNodeFree(obj->value.c);
332 if(obj->type == JSON_STRING)
338 void jsonObjectNodeFree( jsonObjectNode* node ) {
339 if(node == NULL) return;
341 jsonObjectFree(node->item);
345 jsonObject* jsonObjectGetIndex( const jsonObject* obj, unsigned long index ) {
347 if( obj && index >= 0 && obj->type == JSON_ARRAY ) {
349 jsonObjectNode* node = obj->value.c;
351 if(node->index == index)
360 jsonObject* jsonObjectGetKey( const jsonObject* obj, const char* key ) {
362 if( obj && key && obj->type == JSON_HASH ) {
364 jsonObjectNode* node = obj->value.c;
367 if(node->key && !strcmp(node->key, key))
376 char* jsonObjectGetString( const jsonObject* obj ) {
377 if( obj && obj->type == JSON_STRING ) return obj->value.s;
381 double jsonObjectGetNumber( const jsonObject* obj ) {
382 if( obj && obj->type == JSON_NUMBER ) return obj->value.n;
386 void jsonObjectSetString( jsonObject* obj, const char* string) {
388 obj->type = JSON_STRING;
389 if(string) obj->value.s = strdup(string);
390 else obj->value.s = NULL;
395 void jsonObjectSetNumber( jsonObject* obj, double num) {
397 obj->type = JSON_NUMBER;
403 void jsonObjectSetClass( jsonObject* obj, const char* classname) {
404 if( obj == NULL || classname == NULL ) return;
405 obj->classname = strdup(classname);
410 char* jsonObjectToJSON( const jsonObject* obj ) {
412 if(obj == NULL) return strdup("null");
414 growing_buffer* buf = buffer_init(64);
416 /* add class hints if we have a class name */
418 buffer_add(buf,"/*--S ");
419 buffer_add(buf,obj->classname);
420 buffer_add(buf, "--*/");
423 switch( obj->type ) {
426 if(obj->value.b) buffer_add(buf, "true");
427 else buffer_add(buf, "false");
431 double x = obj->value.n;
433 /* if the number does not need to be a double,
434 turn it into an int on the way out */
436 INT_TO_STRING((int)x);
437 buffer_add(buf, INTSTR);
441 buffer_add(buf, DOUBLESTR);
447 buffer_add(buf, "null");
451 buffer_add(buf, "\"");
452 char* data = obj->value.s;
453 int len = strlen(data);
455 char* output = uescape(data, len, 1);
456 buffer_add(buf, output);
458 buffer_add(buf, "\"");
462 buffer_add(buf, "[");
464 for( i = 0; i!= obj->size; i++ ) {
465 const jsonObject* x = jsonObjectGetIndex(obj,i);
466 char* data = jsonObjectToJSON(x);
468 #ifdef STRICT_JSON_WRITE
469 buffer_add(buf, data);
471 if(strcmp(data,"null")) /* only add the string if it isn't null */
472 buffer_add(buf, data);
476 if(i != obj->size - 1)
477 buffer_add(buf, ",");
479 buffer_add(buf, "]");
484 buffer_add(buf, "{");
485 jsonObjectIterator* itr = jsonNewObjectIterator(obj);
488 while( (tmp = jsonObjectIteratorNext(itr)) ) {
490 buffer_add(buf, "\"");
491 buffer_add(buf, tmp->key);
492 buffer_add(buf, "\":");
493 char* data = jsonObjectToJSON(tmp->item);
495 #ifdef STRICT_JSON_WRITE
496 buffer_add(buf, data);
498 if(strcmp(data,"null")) /* only add the string if it isn't null */
499 buffer_add(buf, data);
502 if(jsonObjectIteratorHasNext(itr))
503 buffer_add(buf, ",");
507 jsonObjectIteratorFree(itr);
508 buffer_add(buf, "}");
512 fprintf(stderr, "Unknown object type %d\n", obj->type);
517 /* close out the object hint */
519 buffer_add(buf, "/*--E ");
520 buffer_add(buf, obj->classname);
521 buffer_add(buf, "--*/");
525 buffer_add(buf, " /*");
526 buffer_add(buf, obj->comment);
527 buffer_add(buf, "*/");
530 char* data = buffer_data(buf);
537 void jsonObjectSetComment( jsonObject* obj, const char* com) {
538 if( obj == NULL || com == NULL ) return;
539 obj->comment = strdup(com);
543 char* __tabs(int count) {
544 growing_buffer* buf = buffer_init(24);
546 for(i=0;i!=count;i++) buffer_add(buf, " ");
547 char* final = buffer_data( buf );
552 char* jsonFormatString( const char* string ) {
554 if(!string) return strdup("");
556 growing_buffer* buf = buffer_init(64);
561 for(i=0; i!= strlen(string); i++) {
563 if( string[i] == '{' || string[i] == '[' ) {
565 tab = __tabs(++depth);
566 buffer_fadd( buf, "%c\n%s", string[i], tab);
569 } else if( string[i] == '}' || string[i] == ']' ) {
571 tab = __tabs(--depth);
572 buffer_fadd( buf, "\n%s%c", tab, string[i]);
575 if(string[i+1] != ',') {
577 buffer_fadd( buf, "\n%s", tab );
581 } else if( string[i] == ',' ) {
584 buffer_fadd(buf, ",\n%s", tab);
587 } else { buffer_add_char(buf, string[i]); }
591 char* result = buffer_data(buf);
598 jsonObject* jsonObjectClone(const jsonObject* o) {
600 char* json = jsonObjectToJSON(o);
601 jsonObject* newo = jsonParseString(json);
608 /* ---------------------------------------------------------------------- */
611 jsonObjectIterator* jsonNewObjectIterator(const jsonObject* obj) {
613 if(!obj) return NULL;
614 jsonObjectIterator* iter = safe_malloc(sizeof(jsonObjectIterator));
617 if( obj->type == JSON_HASH || obj->type == JSON_ARRAY )
618 iter->current = obj->value.c;
619 else iter->current = NULL;
623 jsonObjectNode* jsonObjectIteratorNext( jsonObjectIterator* itr ) {
624 if( itr == NULL ) return NULL;
626 jsonObjectNode* tmp = itr->current;
627 if(tmp == NULL) return NULL;
628 itr->current = itr->current->next;
633 void jsonObjectIteratorFree(jsonObjectIterator* iter) {
637 int jsonObjectIteratorHasNext(const jsonObjectIterator* itr) {
638 return (itr && itr->current);
642 jsonObject* jsonObjectFindPath( const jsonObject* obj, char* format, ...) {
643 if(!obj || !format || strlen(format) < 1) return NULL;
645 VA_LIST_TO_STRING(format);
648 /* tmp storage for strtok_r */
656 /* copy the path before strtok_r destroys it */
657 char* pathcopy = strdup(buf);
659 /* grab the root of the path */
660 token = strtok_r(t, "/", &tt);
661 if(!token) return NULL;
663 /* special case where path starts with // (start anywhere) */
664 if(strlen(pathcopy) > 2 && pathcopy[0] == '/' && pathcopy[1] == '/') {
665 jsonObject* it = _jsonObjectFindPathRecurse(obj, token, pathcopy + 1);
674 obj = jsonObjectGetKey(obj, token);
675 } while( (token = strtok_r(NULL, "/", &tt)) && obj);
677 return jsonObjectClone(obj);
680 /* --------------------------------------------------------------- */
684 jsonObject* _jsonObjectFindPathRecurse(const jsonObject* obj, char* root, char* path) {
686 if(!obj || ! root || !path) return NULL;
688 /* collect all of the potential objects */
689 jsonObject* arr = __jsonObjectFindPathRecurse(obj, root);
691 /* container for fully matching objects */
692 jsonObject* newarr = jsonParseString("[]");
695 /* path is just /root or /root/ */
696 if( strlen(root) + 2 >= strlen(path) ) {
701 /* gather all of the sub-objects that match the full path */
702 for( i = 0; i < arr->size; i++ ) {
703 jsonObject* a = jsonObjectGetIndex(arr, i);
704 jsonObject* thing = jsonObjectFindPath(a , path + strlen(root) + 1);
705 if(thing) jsonObjectPush(newarr, thing);
713 jsonObject* __jsonObjectFindPathRecurse(const jsonObject* obj, char* root) {
715 jsonObject* arr = jsonParseString("[]");
720 /* if the current object has a node that matches, add it */
722 jsonObject* o = jsonObjectGetKey(obj, root);
723 if(o) jsonObjectPush( arr, jsonObjectClone(o) );
725 jsonObjectNode* tmp = NULL;
726 jsonObject* childarr;
727 jsonObjectIterator* itr = jsonNewObjectIterator(obj);
729 /* recurse through the children and find all potential nodes */
730 while( (tmp = jsonObjectIteratorNext(itr)) ) {
731 childarr = __jsonObjectFindPathRecurse(tmp->item, root);
732 if(childarr && childarr->size > 0) {
733 for( i = 0; i!= childarr->size; i++ ) {
734 jsonObjectPush( arr, jsonObjectClone(jsonObjectGetIndex(childarr, i)) );
737 jsonObjectFree(childarr);
740 jsonObjectIteratorFree(itr);
746 char* jsonObjectToSimpleString( const jsonObject* o ) {
753 DOUBLE_TO_STRING(o->value.n);
754 value = strdup(DOUBLESTR);
759 value = strdup(o->value.s);