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 #include <opensrf/osrf_legacy_json.h>
19 /* keep a copy of the length of the current json string so we don't
20 * have to calculate it in each function
25 jsonObject* legacy_jsonParseString( const char* string) {
26 return json_parse_string( (char*) string );
29 jsonObject* legacy_jsonParseStringFmt( const char* string, ... ) {
30 VA_LIST_TO_STRING(string);
31 return json_parse_string( VA_BUF );
35 jsonObject* json_parse_string(char* string) {
37 if(string == NULL) return NULL;
39 current_strlen = strlen(string);
41 if(current_strlen == 0)
44 unsigned long index = 0;
46 json_eat_ws(string, &index, 1, current_strlen); /* remove leading whitespace */
47 if(index == current_strlen) return NULL;
49 jsonObject* obj = jsonNewObject(NULL);
51 int status = _json_parse_string(string, &index, obj, current_strlen);
52 if(!status) return obj;
63 int _json_parse_string(char* string, unsigned long* index, jsonObject* obj, int current_strlen) {
64 if( !string || !index || *index >= current_strlen) return -2;
66 int status = 0; /* return code from parsing routines */
67 char* classname = NULL; /* object class hint */
68 json_eat_ws(string, index, 1, current_strlen); /* remove leading whitespace */
70 char c = string[*index];
72 /* remove any leading comments */
76 (*index)++; /* move to second comment char */
77 status = json_eat_comment(string, index, &classname, 1, current_strlen);
78 if(status) return status;
80 json_eat_ws(string, index, 1, current_strlen);
87 json_eat_ws(string, index, 1, current_strlen); /* remove leading whitespace */
89 if(*index >= current_strlen)
97 status = json_parse_json_string(string, index, obj, current_strlen);
103 status = json_parse_json_array(string, index, obj, current_strlen);
109 status = json_parse_json_object(string, index, obj, current_strlen);
115 status = json_parse_json_null(string, index, obj, current_strlen);
124 status = json_parse_json_bool(string, index, obj, current_strlen);
128 if(isdigit(c) || c == '.' || c == '-') { /* are we a number? */
129 status = json_parse_json_number(string, index, obj, current_strlen);
130 if(status) return status;
135 /* we should never get here */
136 return json_handle_error(string, index, "_json_parse_string() final switch clause");
139 if(status) return status;
141 json_eat_ws(string, index, 1, current_strlen);
143 if( *index < current_strlen ) {
144 /* remove any trailing comments */
148 status = json_eat_comment(string, index, NULL, 0, current_strlen);
149 if(status) return status;
154 jsonObjectSetClass(obj, classname);
162 int json_parse_json_null(char* string, unsigned long* index, jsonObject* obj, int current_strlen) {
164 if(*index >= (current_strlen - 3)) {
165 return json_handle_error(string, index,
166 "_parse_json_null(): invalid null" );
169 if(!strncasecmp(string + (*index), "null", 4)) {
171 obj->type = JSON_NULL;
174 return json_handle_error(string, index,
175 "_parse_json_null(): invalid null" );
179 /* should be at the first character of the bool at this point */
180 int json_parse_json_bool(char* string, unsigned long* index, jsonObject* obj, int current_strlen) {
181 if( ! string || ! obj || *index >= current_strlen ) return -1;
183 char* ret = "json_parse_json_bool(): truncated bool";
185 if( *index >= (current_strlen - 5))
186 return json_handle_error(string, index, ret);
188 if(!strncasecmp( string + (*index), "false", 5)) {
191 obj->type = JSON_BOOL;
195 if( *index >= (current_strlen - 4))
196 return json_handle_error(string, index, ret);
198 if(!strncasecmp( string + (*index), "true", 4)) {
201 obj->type = JSON_BOOL;
205 return json_handle_error(string, index, ret);
209 /* expecting the first character of the number */
210 int json_parse_json_number(char* string, unsigned long* index, jsonObject* obj, int current_strlen) {
211 if( ! string || ! obj || *index >= current_strlen ) return -1;
213 growing_buffer* buf = buffer_init(64);
214 char c = string[*index];
219 /* negative number? */
220 if(c == '-') { buffer_add(buf, "-"); (*index)++; }
224 while(*index < current_strlen) {
227 buffer_add_char(buf, c);
230 else if( c == '.' ) {
233 return json_handle_error(string, index,
234 "json_parse_json_number(): malformed json number");
237 buffer_add_char(buf, c);
247 obj->type = JSON_NUMBER;
248 obj->value.n = strtod(buf->buf, NULL);
253 /* index should point to the character directly following the '['. when done
254 * index will point to the character directly following the ']' character
256 int json_parse_json_array(char* string, unsigned long* index, jsonObject* obj, int current_strlen) {
258 if( ! string || ! obj || ! index || *index >= current_strlen ) return -1;
261 int in_parse = 0; /* true if this array already contains one item */
262 obj->type = JSON_ARRAY;
266 while(*index < current_strlen) {
268 json_eat_ws(string, index, 1, current_strlen);
270 if(string[*index] == ']') {
277 json_eat_ws(string, index, 1, current_strlen);
278 if(string[*index] != ',') {
279 return json_handle_error(string, index,
280 "json_parse_json_array(): array item not followed by a ','");
283 json_eat_ws(string, index, 1, current_strlen);
286 jsonObject* item = jsonNewObject(NULL);
288 #ifndef STRICT_JSON_READ
289 if(*index < current_strlen) {
290 if(string[*index] == ',' || string[*index] == ']') {
295 if(!set) status = _json_parse_string(string, index, item, current_strlen);
298 status = _json_parse_string(string, index, item, current_strlen);
301 if(status) { jsonObjectFree(item); return status; }
302 jsonObjectPush(obj, item);
308 return json_handle_error(string, index,
309 "json_parse_json_array(): array not closed");
315 /* index should point to the character directly following the '{'. when done
316 * index will point to the character directly following the '}'
318 int json_parse_json_object(char* string, unsigned long* index, jsonObject* obj, int current_strlen) {
319 if( ! string || !obj || ! index || *index >= current_strlen ) return -1;
321 obj->type = JSON_HASH;
323 int in_parse = 0; /* true if we've already added one item to this object */
327 while(*index < current_strlen) {
329 json_eat_ws(string, index, 1, current_strlen);
331 if(string[*index] == '}') {
338 if(string[*index] != ',') {
339 return json_handle_error(string, index,
340 "json_parse_json_object(): object missing ',' between elements" );
343 json_eat_ws(string, index, 1, current_strlen);
346 /* first we grab the hash key */
347 jsonObject* key_obj = jsonNewObject(NULL);
348 status = _json_parse_string(string, index, key_obj, current_strlen);
349 if(status) return status;
351 if(key_obj->type != JSON_STRING) {
352 return json_handle_error(string, index,
353 "_json_parse_json_object(): hash key not a string");
356 char* key = key_obj->value.s;
358 json_eat_ws(string, index, 1, current_strlen);
360 if(string[*index] != ':') {
361 return json_handle_error(string, index,
362 "json_parse_json_object(): hash key not followed by ':' character");
367 /* now grab the value object */
368 json_eat_ws(string, index, 1, current_strlen);
369 jsonObject* value_obj = jsonNewObject(NULL);
371 #ifndef STRICT_JSON_READ
372 if(*index < current_strlen) {
373 if(string[*index] == ',' || string[*index] == '}') {
379 status = _json_parse_string(string, index, value_obj, current_strlen);
382 status = _json_parse_string(string, index, value_obj, current_strlen);
385 if(status) return status;
387 /* put the data into the object and continue */
388 jsonObjectSetKey(obj, key, value_obj);
389 jsonObjectFree(key_obj);
395 return json_handle_error(string, index,
396 "json_parse_json_object(): object not closed");
403 /* when done, index will point to the character after the closing quote */
404 int json_parse_json_string(char* string, unsigned long* index, jsonObject* obj, int current_strlen) {
405 if( ! string || ! index || *index >= current_strlen ) return -1;
409 growing_buffer* buf = buffer_init(64);
411 while(*index < current_strlen) {
413 char c = string[*index];
419 buffer_add(buf, "\\");
427 buffer_add(buf, "\"");
435 buffer_add(buf,"\t");
438 buffer_add_char(buf, c);
443 buffer_add(buf,"\b");
446 buffer_add_char(buf, c);
451 buffer_add(buf,"\f");
454 buffer_add_char(buf, c);
459 buffer_add(buf,"\r");
462 buffer_add_char(buf, c);
467 buffer_add(buf,"\n");
470 buffer_add_char(buf, c);
477 if(*index >= (current_strlen - 4)) {
479 return json_handle_error(string, index,
480 "json_parse_json_string(): truncated escaped unicode"); }
483 osrf_clearbuf(buff, sizeof(buff));
484 memcpy(buff, string + (*index), 4);
487 /* ----------------------------------------------------------------------- */
488 /* ----------------------------------------------------------------------- */
489 /* The following chunk was borrowed with permission from
490 json-c http://oss.metaparadigm.com/json-c/ */
491 unsigned char utf_out[3];
492 memset(utf_out, 0, sizeof(utf_out));
494 #define hexdigit(x) ( ((x) <= '9') ? (x) - '0' : ((x) & 7) + 9)
496 unsigned int ucs_char =
497 (hexdigit(string[*index] ) << 12) +
498 (hexdigit(string[*index + 1]) << 8) +
499 (hexdigit(string[*index + 2]) << 4) +
500 hexdigit(string[*index + 3]);
502 if (ucs_char < 0x80) {
503 utf_out[0] = ucs_char;
504 buffer_add(buf, (char*) utf_out);
506 } else if (ucs_char < 0x800) {
507 utf_out[0] = 0xc0 | (ucs_char >> 6);
508 utf_out[1] = 0x80 | (ucs_char & 0x3f);
509 buffer_add(buf, (char*) utf_out);
512 utf_out[0] = 0xe0 | (ucs_char >> 12);
513 utf_out[1] = 0x80 | ((ucs_char >> 6) & 0x3f);
514 utf_out[2] = 0x80 | (ucs_char & 0x3f);
515 buffer_add(buf, (char*) utf_out);
517 /* ----------------------------------------------------------------------- */
518 /* ----------------------------------------------------------------------- */
525 buffer_add_char(buf, c);
531 buffer_add_char(buf, c);
538 jsonObjectSetString(obj, buf->buf);
544 void json_eat_ws(char* string, unsigned long* index, int eat_all, int current_strlen) {
545 if( ! string || ! index ) return;
546 if(*index >= current_strlen)
549 if( eat_all ) { /* removes newlines, etc */
550 while(string[*index] == ' ' ||
551 string[*index] == '\n' ||
552 string[*index] == '\t')
557 while(string[*index] == ' ') (*index)++;
561 /* index should be at the '*' character at the beginning of the comment.
562 * when done, index will point to the first character after the final /
564 int json_eat_comment(char* string, unsigned long* index, char** buffer, int parse_class, int current_strlen) {
565 if( ! string || ! index || *index >= current_strlen ) return -1;
568 if(string[*index] != '*' && string[*index] != '/' )
569 return json_handle_error(string, index,
570 "json_eat_comment(): invalid character after /");
572 /* chop out any // style comments */
573 if(string[*index] == '/') {
575 char c = string[*index];
576 while(*index < current_strlen) {
587 int on_star = 0; /* true if we just saw a '*' character */
589 /* we're just past the '*' */
590 if(!parse_class) { /* we're not concerned with class hints */
591 while(*index < current_strlen) {
592 if(string[*index] == '/') {
599 if(string[*index] == '*') on_star = 1;
609 growing_buffer* buf = buffer_init(64);
619 /*--S hint--*/ /* <-- Hints look like this */
622 while(*index < current_strlen) {
623 char c = string[*index];
629 if(third_dash) fourth_dash = 1;
630 else if(in_hint) third_dash = 1;
631 else if(first_dash) second_dash = 1;
637 if(second_dash && !in_hint) {
639 json_eat_ws(string, index, 1, current_strlen);
640 (*index)--; /* this will get incremented at the bottom of the loop */
645 if(second_dash && in_hint) {
646 buffer_add_char(buf, c);
652 if(second_dash && !in_hint) {
654 json_eat_ws(string, index, 1, current_strlen);
655 (*index)--; /* this will get incremented at the bottom of the loop */
660 if(second_dash && in_hint) {
661 buffer_add_char(buf, c);
679 buffer_add_char(buf, c);
686 if( buf->n_used > 0 && buffer)
687 *buffer = buffer_data(buf);
693 int json_handle_error(char* string, unsigned long* index, char* err_msg) {
696 osrf_clearbuf(buf, sizeof(buf));
699 strncpy( buf, string + (*index - 30), 59 );
701 strncpy( buf, string, 59 );
704 "\nError parsing json string at charracter %c "
705 "(code %d) and index %ld\nString length: %d\nMsg:\t%s\nNear:\t%s\nFull String:\t%s\n",
706 string[*index], string[*index], *index, current_strlen, err_msg, buf, string );
712 jsonObject* legacy_jsonParseFile( const char* filename ) {
713 return json_parse_file( filename );
716 jsonObject* json_parse_file(const char* filename) {
717 if(!filename) return NULL;
718 char* data = file_to_string(filename);
719 jsonObject* o = json_parse_string(data);
726 char* legacy_jsonObjectToJSON( const jsonObject* obj ) {
728 if(obj == NULL) return strdup("null");
730 growing_buffer* buf = buffer_init(64);
732 /* add class hints if we have a class name */
734 buffer_add(buf,"/*--S ");
735 buffer_add(buf,obj->classname);
736 buffer_add(buf, "--*/");
739 switch( obj->type ) {
742 if(obj->value.b) buffer_add(buf, "true");
743 else buffer_add(buf, "false");
747 double x = obj->value.n;
749 /* if the number does not need to be a double,
750 turn it into an int on the way out */
752 INT_TO_STRING((int)x);
753 buffer_add(buf, INTSTR);
757 buffer_add(buf, DOUBLESTR);
763 buffer_add(buf, "null");
767 buffer_add(buf, "\"");
768 char* data = obj->value.s;
769 int len = strlen(data);
771 char* output = uescape(data, len, 1);
772 buffer_add(buf, output);
774 buffer_add(buf, "\"");
778 buffer_add(buf, "[");
780 for( i = 0; i!= obj->size; i++ ) {
781 const jsonObject* x = jsonObjectGetIndex(obj,i);
782 char* data = legacy_jsonObjectToJSON(x);
783 buffer_add(buf, data);
785 if(i != obj->size - 1)
786 buffer_add(buf, ",");
788 buffer_add(buf, "]");
793 buffer_add(buf, "{");
794 jsonIterator* itr = jsonNewIterator(obj);
797 while( (tmp = jsonIteratorNext(itr)) ) {
799 buffer_add(buf, "\"");
801 char* key = itr->key;
802 int len = strlen(key);
803 char* output = uescape(key, len, 1);
804 buffer_add(buf, output);
807 buffer_add(buf, "\":");
808 char* data = legacy_jsonObjectToJSON(tmp);
809 buffer_add(buf, data);
810 if(jsonIteratorHasNext(itr))
811 buffer_add(buf, ",");
815 jsonIteratorFree(itr);
816 buffer_add(buf, "}");
820 fprintf(stderr, "Unknown object type %d\n", obj->type);
825 /* close out the object hint */
827 buffer_add(buf, "/*--E ");
828 buffer_add(buf, obj->classname);
829 buffer_add(buf, "--*/");
832 char* data = buffer_data(buf);
839 static jsonObjectNode* makeNode(jsonObject* obj, unsigned long index, char* key) {
840 jsonObjectNode* node = safe_malloc(sizeof(jsonObjectNode));
847 jsonObjectIterator* jsonNewObjectIterator(const jsonObject* obj) {
848 if(!obj) return NULL;
849 jsonObjectIterator* itr = safe_malloc(sizeof(jsonObjectIterator));
850 itr->iterator = jsonNewIterator(obj);
857 jsonObjectNode* jsonObjectIteratorNext( jsonObjectIterator* itr ) {
858 if(itr == NULL || itr->done) return NULL;
860 if(itr->current) free(itr->current);
861 jsonObject* next = jsonIteratorNext(itr->iterator);
867 itr->current = makeNode(next, itr->iterator->index, itr->iterator->key);
871 void jsonObjectIteratorFree(jsonObjectIterator* iter) {
872 if(iter->current) free(iter->current);
873 jsonIteratorFree(iter->iterator);
877 int jsonObjectIteratorHasNext(const jsonObjectIterator* itr) {
878 return (itr && itr->current);