]> git.evergreen-ils.org Git - OpenSRF.git/blob - src/libopensrf/osrf_legacy_json.c
Merging changes from branches/new-json2
[OpenSRF.git] / src / libopensrf / osrf_legacy_json.c
1 /*
2 Copyright (C) 2006  Georgia Public Library Service 
3 Bill Erickson <billserickson@gmail.com>
4
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.
9
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.
14 */
15
16
17 #include <opensrf/osrf_legacy_json.h>
18
19 /* keep a copy of the length of the current json string so we don't 
20  * have to calculate it in each function
21  */
22 int current_strlen; 
23
24
25 jsonObject* legacy_jsonParseString( char* string) {
26         return json_parse_string( string );
27 }
28
29 jsonObject* legacy_jsonParseStringFmt( char* string, ... ) {
30         VA_LIST_TO_STRING(string);
31         return json_parse_string( VA_BUF );
32 }
33
34
35 jsonObject* json_parse_string(char* string) {
36
37         if(string == NULL) return NULL;
38
39         current_strlen = strlen(string);
40
41         if(current_strlen == 0) 
42                 return NULL;
43
44         unsigned long index = 0;
45
46         json_eat_ws(string, &index, 1, current_strlen); /* remove leading whitespace */
47         if(index == current_strlen) return NULL;
48
49         jsonObject* obj = jsonNewObject(NULL);
50
51         int status = _json_parse_string(string, &index, obj, current_strlen);
52         if(!status) return obj;
53
54         if(status == -2) {
55                 jsonObjectFree(obj);
56                 return NULL;
57         }
58
59         return NULL;
60 }
61
62
63 int _json_parse_string(char* string, unsigned long* index, jsonObject* obj, int current_strlen) {
64         if( !string || !index || *index >= current_strlen) return -2;
65
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 */
69
70         char c = string[*index];
71
72         /* remove any leading comments */
73         if( c == '/' ) { 
74
75                 while(1) {
76                         (*index)++; /* move to second comment char */
77                         status = json_eat_comment(string, index, &classname, 1, current_strlen);
78                         if(status) return status;
79
80                         json_eat_ws(string, index, 1, current_strlen);
81                         c = string[*index];
82                         if(c != '/')
83                                 break;
84                 }
85         }
86
87         json_eat_ws(string, index, 1, current_strlen); /* remove leading whitespace */
88
89         if(*index >= current_strlen)
90                 return -2;
91
92         switch(c) {
93                                 
94                 /* json string */
95                 case '"': 
96                         (*index)++;
97                         status = json_parse_json_string(string, index, obj, current_strlen); break;
98
99                 /* json array */
100                 case '[':
101                         (*index)++;
102                         status = json_parse_json_array(string, index, obj, current_strlen);                     
103                         break;
104
105                 /* json object */
106                 case '{':
107                         (*index)++;
108                         status = json_parse_json_object(string, index, obj, current_strlen);
109                         break;
110
111                 /* NULL */
112                 case 'n':
113                 case 'N':
114                         status = json_parse_json_null(string, index, obj, current_strlen);
115                         break;
116                         
117
118                 /* true, false */
119                 case 'f':
120                 case 'F':
121                 case 't':
122                 case 'T':
123                         status = json_parse_json_bool(string, index, obj, current_strlen);
124                         break;
125
126                 default:
127                         if(isdigit(c) || c == '.' || c == '-') { /* are we a number? */
128                                 status = json_parse_json_number(string, index, obj, current_strlen);    
129                                 if(status) return status;
130                                 break;
131                         }
132
133                         (*index)--;
134                         /* we should never get here */
135                         return json_handle_error(string, index, "_json_parse_string() final switch clause");
136         }       
137
138         if(status) return status;
139
140         json_eat_ws(string, index, 1, current_strlen);
141
142         if( *index < current_strlen ) {
143                 /* remove any trailing comments */
144                 c = string[*index];
145                 if( c == '/' ) { 
146                         (*index)++;
147                         status = json_eat_comment(string, index, NULL, 0, current_strlen);
148                         if(status) return status;
149                 }
150         }
151
152         if(classname){
153                 jsonObjectSetClass(obj, classname);
154                 free(classname);
155         }
156
157         return 0;
158 }
159
160
161 int json_parse_json_null(char* string, unsigned long* index, jsonObject* obj, int current_strlen) {
162
163         if(*index >= (current_strlen - 3)) {
164                 return json_handle_error(string, index, 
165                         "_parse_json_null(): invalid null" );
166         }
167
168         if(!strncasecmp(string + (*index), "null", 4)) {
169                 (*index) += 4;
170                 obj->type = JSON_NULL;
171                 return 0;
172         } else {
173                 return json_handle_error(string, index,
174                         "_parse_json_null(): invalid null" );
175         }
176 }
177
178 /* should be at the first character of the bool at this point */
179 int json_parse_json_bool(char* string, unsigned long* index, jsonObject* obj, int current_strlen) {
180         if( ! string || ! obj || *index >= current_strlen ) return -1;
181
182         char* ret = "json_parse_json_bool(): truncated bool";
183
184         if( *index >= (current_strlen - 5))
185                 return json_handle_error(string, index, ret);
186         
187         if(!strncasecmp( string + (*index), "false", 5)) {
188                 (*index) += 5;
189                 obj->value.b = 0;
190                 obj->type = JSON_BOOL;
191                 return 0;
192         }
193
194         if( *index >= (current_strlen - 4))
195                 return json_handle_error(string, index, ret);
196
197         if(!strncasecmp( string + (*index), "true", 4)) {
198                 (*index) += 4;
199                 obj->value.b = 1;
200                 obj->type = JSON_BOOL;
201                 return 0;
202         }
203
204         return json_handle_error(string, index, ret);
205 }
206
207
208 /* expecting the first character of the number */
209 int json_parse_json_number(char* string, unsigned long* index, jsonObject* obj, int current_strlen) {
210         if( ! string || ! obj || *index >= current_strlen ) return -1;
211
212         growing_buffer* buf = buffer_init(64);
213         char c = string[*index];
214
215         int done = 0;
216         int dot_seen = 0;
217
218         /* negative number? */
219         if(c == '-') { buffer_add(buf, "-"); (*index)++; }
220
221         c = string[*index];
222
223         while(*index < current_strlen) {
224
225                 if(isdigit(c)) {
226                         buffer_add_char(buf, c);
227                 }
228
229                 else if( c == '.' ) {
230                         if(dot_seen) {
231                                 buffer_free(buf);
232                                 return json_handle_error(string, index, 
233                                         "json_parse_json_number(): malformed json number");
234                         }
235                         dot_seen = 1;
236                         buffer_add_char(buf, c);
237                 } else {
238                         done = 1; break;
239                 }
240
241                 (*index)++;
242                 c = string[*index];
243                 if(done) break;
244         }
245
246         obj->type = JSON_NUMBER;
247         obj->value.n = strtod(buf->buf, NULL);
248         buffer_free(buf);
249         return 0;
250 }
251
252 /* index should point to the character directly following the '['.  when done
253  * index will point to the character directly following the ']' character
254  */
255 int json_parse_json_array(char* string, unsigned long* index, jsonObject* obj, int current_strlen) {
256
257         if( ! string || ! obj || ! index || *index >= current_strlen ) return -1;
258
259         int status = 0;
260         int in_parse = 0; /* true if this array already contains one item */
261         obj->type = JSON_ARRAY;
262         int set = 0;
263         int done = 0;
264
265         while(*index < current_strlen) {
266
267                 json_eat_ws(string, index, 1, current_strlen);
268
269                 if(string[*index] == ']') {
270                         (*index)++;
271                         done = 1;
272                         break;
273                 }
274
275                 if(in_parse) {
276                         json_eat_ws(string, index, 1, current_strlen);
277                         if(string[*index] != ',') {
278                                 return json_handle_error(string, index,
279                                         "json_parse_json_array(): array item not followed by a ','");
280                         }
281                         (*index)++;
282                         json_eat_ws(string, index, 1, current_strlen);
283                 }
284
285                 jsonObject* item = jsonNewObject(NULL);
286
287                 #ifndef STRICT_JSON_READ
288                 if(*index < current_strlen) {
289                         if(string[*index] == ',' || string[*index] == ']') {
290                                 status = 0;
291                                 set = 1;
292                         }
293                 }
294                 if(!set) status = _json_parse_string(string, index, item, current_strlen);
295
296                 #else
297                 status = _json_parse_string(string, index, item, current_strlen);
298                 #endif
299
300                 if(status) { jsonObjectFree(item); return status; }
301                 jsonObjectPush(obj, item);
302                 in_parse = 1;
303                 set = 0;
304         }
305
306         if(!done)
307                 return json_handle_error(string, index,
308                         "json_parse_json_array(): array not closed");
309
310         return 0;
311 }
312
313
314 /* index should point to the character directly following the '{'.  when done
315  * index will point to the character directly following the '}'
316  */
317 int json_parse_json_object(char* string, unsigned long* index, jsonObject* obj, int current_strlen) {
318         if( ! string || !obj || ! index || *index >= current_strlen ) return -1;
319
320         obj->type = JSON_HASH;
321         int status;
322         int in_parse = 0; /* true if we've already added one item to this object */
323         int set = 0;
324         int done = 0;
325
326         while(*index < current_strlen) {
327
328                 json_eat_ws(string, index, 1, current_strlen);
329
330                 if(string[*index] == '}') {
331                         (*index)++;
332                         done = 1;
333                         break;
334                 }
335
336                 if(in_parse) {
337                         if(string[*index] != ',') {
338                                 return json_handle_error(string, index,
339                                         "json_parse_json_object(): object missing ',' between elements" );
340                         }
341                         (*index)++;
342                         json_eat_ws(string, index, 1, current_strlen);
343                 }
344
345                 /* first we grab the hash key */
346                 jsonObject* key_obj = jsonNewObject(NULL);
347                 status = _json_parse_string(string, index, key_obj, current_strlen);
348                 if(status) return status;
349
350                 if(key_obj->type != JSON_STRING) {
351                         return json_handle_error(string, index, 
352                                 "_json_parse_json_object(): hash key not a string");
353                 }
354
355                 char* key = key_obj->value.s;
356
357                 json_eat_ws(string, index, 1, current_strlen);
358
359                 if(string[*index] != ':') {
360                         return json_handle_error(string, index, 
361                                 "json_parse_json_object(): hash key not followed by ':' character");
362                 }
363
364                 (*index)++;
365
366                 /* now grab the value object */
367                 json_eat_ws(string, index, 1, current_strlen);
368                 jsonObject* value_obj = jsonNewObject(NULL);
369
370 #ifndef STRICT_JSON_READ
371                 if(*index < current_strlen) {
372                         if(string[*index] == ',' || string[*index] == '}') {
373                                 status = 0;
374                                 set = 1;
375                         }
376                 }
377                 if(!set)
378                         status = _json_parse_string(string, index, value_obj, current_strlen);
379
380 #else
381                  status = _json_parse_string(string, index, value_obj, current_strlen);
382 #endif
383
384                 if(status) return status;
385
386                 /* put the data into the object and continue */
387                 jsonObjectSetKey(obj, key, value_obj);
388                 jsonObjectFree(key_obj);
389                 in_parse = 1;
390                 set = 0;
391         }
392
393         if(!done)
394                 return json_handle_error(string, index,
395                         "json_parse_json_object(): object not closed");
396
397         return 0;
398 }
399
400
401
402 /* when done, index will point to the character after the closing quote */
403 int json_parse_json_string(char* string, unsigned long* index, jsonObject* obj, int current_strlen) {
404         if( ! string || ! index || *index >= current_strlen ) return -1;
405
406         int in_escape = 0;      
407         int done = 0;
408         growing_buffer* buf = buffer_init(64);
409
410         while(*index < current_strlen) {
411
412                 char c = string[*index]; 
413
414                 switch(c) {
415
416                         case '\\':
417                                 if(in_escape) {
418                                         buffer_add(buf, "\\");
419                                         in_escape = 0;
420                                 } else 
421                                         in_escape = 1;
422                                 break;
423
424                         case '"':
425                                 if(in_escape) {
426                                         buffer_add(buf, "\"");
427                                         in_escape = 0;
428                                 } else 
429                                         done = 1;
430                                 break;
431
432                         case 't':
433                                 if(in_escape) {
434                                         buffer_add(buf,"\t");
435                                         in_escape = 0;
436                                 } else 
437                                         buffer_add_char(buf, c);
438                                 break;
439
440                         case 'b':
441                                 if(in_escape) {
442                                         buffer_add(buf,"\b");
443                                         in_escape = 0;
444                                 } else 
445                                         buffer_add_char(buf, c);
446                                 break;
447
448                         case 'f':
449                                 if(in_escape) {
450                                         buffer_add(buf,"\f");
451                                         in_escape = 0;
452                                 } else 
453                                         buffer_add_char(buf, c);
454                                 break;
455
456                         case 'r':
457                                 if(in_escape) {
458                                         buffer_add(buf,"\r");
459                                         in_escape = 0;
460                                 } else 
461                                         buffer_add_char(buf, c);
462                                 break;
463
464                         case 'n':
465                                 if(in_escape) {
466                                         buffer_add(buf,"\n");
467                                         in_escape = 0;
468                                 } else 
469                                         buffer_add_char(buf, c);
470                                 break;
471
472                         case 'u':
473                                 if(in_escape) {
474                                         (*index)++;
475
476                                         if(*index >= (current_strlen - 4)) {
477                                                 buffer_free(buf);
478                                                 return json_handle_error(string, index,
479                                                         "json_parse_json_string(): truncated escaped unicode"); }
480
481                                         char buff[5];
482                                         memset(buff,0,5);
483                                         memcpy(buff, string + (*index), 4);
484
485
486                                         /* ----------------------------------------------------------------------- */
487                                         /* ----------------------------------------------------------------------- */
488                                         /* The following chunk was borrowed with permission from 
489                                                 json-c http://oss.metaparadigm.com/json-c/ */
490                                         unsigned char utf_out[3];
491                                         memset(utf_out,0,3);
492
493                                         #define hexdigit(x) ( ((x) <= '9') ? (x) - '0' : ((x) & 7) + 9)
494
495                                         unsigned int ucs_char =
496                                                 (hexdigit(string[*index] ) << 12) +
497                                                 (hexdigit(string[*index + 1]) << 8) +
498                                                 (hexdigit(string[*index + 2]) << 4) +
499                                                 hexdigit(string[*index + 3]);
500         
501                                         if (ucs_char < 0x80) {
502                                                 utf_out[0] = ucs_char;
503                                                 buffer_add(buf, (char*) utf_out);
504
505                                         } else if (ucs_char < 0x800) {
506                                                 utf_out[0] = 0xc0 | (ucs_char >> 6);
507                                                 utf_out[1] = 0x80 | (ucs_char & 0x3f);
508                                                 buffer_add(buf, (char*) utf_out);
509
510                                         } else {
511                                                 utf_out[0] = 0xe0 | (ucs_char >> 12);
512                                                 utf_out[1] = 0x80 | ((ucs_char >> 6) & 0x3f);
513                                                 utf_out[2] = 0x80 | (ucs_char & 0x3f);
514                                                 buffer_add(buf, (char*) utf_out);
515                                         }
516                                         /* ----------------------------------------------------------------------- */
517                                         /* ----------------------------------------------------------------------- */
518
519                                         (*index) += 3;
520                                         in_escape = 0;
521
522                                 } else {
523
524                                         buffer_add_char(buf, c);
525                                 }
526
527                                 break;
528
529                         default:
530                                 buffer_add_char(buf, c);
531                 }
532
533                 (*index)++;
534                 if(done) break;
535         }
536
537         jsonObjectSetString(obj, buf->buf);
538         buffer_free(buf);
539         return 0;
540 }
541
542
543 void json_eat_ws(char* string, unsigned long* index, int eat_all, int current_strlen) {
544         if( ! string || ! index ) return;
545         if(*index >= current_strlen)
546                 return;
547
548         if( eat_all ) { /* removes newlines, etc */
549                 while(string[*index] == ' '     || 
550                                 string[*index] == '\n'  ||
551                                 string[*index] == '\t') 
552                         (*index)++;
553         }
554
555         else    
556                 while(string[*index] == ' ') (*index)++;
557 }
558
559
560 /* index should be at the '*' character at the beginning of the comment.
561  * when done, index will point to the first character after the final /
562  */
563 int json_eat_comment(char* string, unsigned long* index, char** buffer, int parse_class, int current_strlen) {
564         if( ! string || ! index || *index >= current_strlen ) return -1;
565         
566
567         if(string[*index] != '*' && string[*index] != '/' )
568                 return json_handle_error(string, index, 
569                         "json_eat_comment(): invalid character after /");
570
571         /* chop out any // style comments */
572         if(string[*index] == '/') {
573                 (*index)++;
574                 char c = string[*index];
575                 while(*index < current_strlen) {
576                         (*index)++;
577                         if(c == '\n') 
578                                 return 0;
579                         c = string[*index];
580                 }
581                 return 0;
582         }
583
584         (*index)++;
585
586         int on_star                     = 0; /* true if we just saw a '*' character */
587
588         /* we're just past the '*' */
589         if(!parse_class) { /* we're not concerned with class hints */
590                 while(*index < current_strlen) {
591                         if(string[*index] == '/') {
592                                 if(on_star) {
593                                         (*index)++;
594                                         return 0;
595                                 }
596                         }
597
598                         if(string[*index] == '*') on_star = 1;
599                         else on_star = 0;
600
601                         (*index)++;
602                 }
603                 return 0;
604         }
605
606
607
608         growing_buffer* buf = buffer_init(64);
609
610         int first_dash          = 0;
611         int second_dash = 0;
612         int third_dash          = 0;
613         int fourth_dash = 0;
614
615         int in_hint                     = 0;
616         int done                                = 0;
617
618         /*--S hint--*/   /* <-- Hints  look like this */
619         /*--E hint--*/
620
621         while(*index < current_strlen) {
622                 char c = string[*index];
623
624                 switch(c) {
625
626                         case '-':
627                                 on_star = 0;
628                                 if(third_dash)                  fourth_dash = 1;
629                                 else if(in_hint)                third_dash      = 1;
630                                 else if(first_dash)     second_dash = 1;
631                                 else                                            first_dash = 1;
632                                 break;
633
634                         case 'S':
635                                 on_star = 0;
636                                 if(second_dash && !in_hint) {
637                                         (*index)++;
638                                         json_eat_ws(string, index, 1, current_strlen);
639                                         (*index)--; /* this will get incremented at the bottom of the loop */
640                                         in_hint = 1;
641                                         break;
642                                 } 
643
644                                 if(second_dash && in_hint) {
645                                         buffer_add_char(buf, c);
646                                         break;
647                                 }
648
649                         case 'E':
650                                 on_star = 0;
651                                 if(second_dash && !in_hint) {
652                                         (*index)++;
653                                         json_eat_ws(string, index, 1, current_strlen);
654                                         (*index)--; /* this will get incremented at the bottom of the loop */
655                                         in_hint = 1;
656                                         break;
657                                 }
658
659                                 if(second_dash && in_hint) {
660                                         buffer_add_char(buf, c);
661                                         break;
662                                 }
663
664                         case '*':
665                                 on_star = 1;
666                                 break;
667
668                         case '/':
669                                 if(on_star) 
670                                         done = 1;
671                                 else
672                                 on_star = 0;
673                                 break;
674
675                         default:
676                                 on_star = 0;
677                                 if(in_hint)
678                                         buffer_add_char(buf, c);
679                 }
680
681                 (*index)++;
682                 if(done) break;
683         }
684
685         if( buf->n_used > 0 && buffer)
686                 *buffer = buffer_data(buf);
687
688         buffer_free(buf);
689         return 0;
690 }
691
692 int json_handle_error(char* string, unsigned long* index, char* err_msg) {
693
694         char buf[60];
695         memset(buf, 0, 60);
696
697         if(*index > 30)
698                 strncpy( buf, string + (*index - 30), 59 );
699         else
700                 strncpy( buf, string, 59 );
701
702         fprintf(stderr, 
703                         "\nError parsing json string at charracter %c "
704                         "(code %d) and index %ld\nString length: %d\nMsg:\t%s\nNear:\t%s\nFull String:\t%s\n", 
705                         string[*index], string[*index], *index, current_strlen, err_msg, buf, string );
706
707         return -1;
708 }
709
710
711 jsonObject* legacy_jsonParseFile( const char* filename ) {
712         return json_parse_file( filename );
713 }
714         
715 jsonObject* json_parse_file(const char* filename) {
716         if(!filename) return NULL;
717         char* data = file_to_string(filename);
718         jsonObject* o = json_parse_string(data);
719         free(data);
720         return o;
721 }
722
723
724
725 char* legacy_jsonObjectToJSON( const jsonObject* obj ) {
726
727         if(obj == NULL) return strdup("null");
728
729         growing_buffer* buf = buffer_init(64);
730
731         /* add class hints if we have a class name */
732         if(obj->classname) {
733                 buffer_add(buf,"/*--S ");
734                 buffer_add(buf,obj->classname);
735                 buffer_add(buf, "--*/");
736         }
737
738         switch( obj->type ) {
739
740                 case JSON_BOOL: 
741                         if(obj->value.b) buffer_add(buf, "true"); 
742                         else buffer_add(buf, "false"); 
743                         break;
744
745                 case JSON_NUMBER: {
746                         double x = obj->value.n;
747
748                         /* if the number does not need to be a double,
749                                 turn it into an int on the way out */
750                         if( x == (int) x ) {
751                                 INT_TO_STRING((int)x);  
752                                 buffer_add(buf, INTSTR);
753
754                         } else {
755                                 DOUBLE_TO_STRING(x);
756                                 buffer_add(buf, DOUBLESTR);
757                         }
758                         break;
759                 }
760
761                 case JSON_NULL:
762                         buffer_add(buf, "null");
763                         break;
764
765                 case JSON_STRING:
766                         buffer_add(buf, "\"");
767                         char* data = obj->value.s;
768                         int len = strlen(data);
769                         
770                         char* output = uescape(data, len, 1);
771                         buffer_add(buf, output);
772                         free(output);
773                         buffer_add(buf, "\"");
774                         break;
775
776                 case JSON_ARRAY:
777                         buffer_add(buf, "[");
778                         int i;
779                         for( i = 0; i!= obj->size; i++ ) {
780                                 const jsonObject* x = jsonObjectGetIndex(obj,i);
781                                 char* data = legacy_jsonObjectToJSON(x);
782                                 buffer_add(buf, data);
783                                 free(data);
784                                 if(i != obj->size - 1)
785                                         buffer_add(buf, ",");
786                         }
787                         buffer_add(buf, "]");
788                         break;  
789
790                 case JSON_HASH:
791         
792                         buffer_add(buf, "{");
793                         jsonIterator* itr = jsonNewIterator(obj);
794                         jsonObject* tmp;
795         
796                         while( (tmp = jsonIteratorNext(itr)) ) {
797
798                                 buffer_add(buf, "\"");
799
800                                 char* key = itr->key;
801                                 int len = strlen(key);
802                                 char* output = uescape(key, len, 1);
803                                 buffer_add(buf, output);
804                                 free(output);
805
806                                 buffer_add(buf, "\":");
807                                 char* data =  legacy_jsonObjectToJSON(tmp);
808                                 buffer_add(buf, data);
809                                 if(jsonIteratorHasNext(itr))
810                                         buffer_add(buf, ",");
811                                 free(data);
812                         }
813
814                         jsonIteratorFree(itr);
815                         buffer_add(buf, "}");
816                         break;
817                 
818                         default:
819                                 fprintf(stderr, "Unknown object type %d\n", obj->type);
820                                 break;
821                                 
822         }
823
824         /* close out the object hint */
825         if(obj->classname) {
826                 buffer_add(buf, "/*--E ");
827                 buffer_add(buf, obj->classname);
828                 buffer_add(buf, "--*/");
829         }
830
831         char* data = buffer_data(buf);
832         buffer_free(buf);
833         return data;
834 }
835
836
837
838 static jsonObjectNode* makeNode(jsonObject* obj, unsigned long index, char* key) {
839     jsonObjectNode* node = safe_malloc(sizeof(jsonObjectNode));
840     node->item = obj;
841     node->index = index;
842     node->key = key;
843     return node;
844 }
845
846 jsonObjectIterator* jsonNewObjectIterator(const jsonObject* obj) {
847         if(!obj) return NULL;
848         jsonObjectIterator* itr = safe_malloc(sizeof(jsonObjectIterator));
849     itr->iterator = jsonNewIterator(obj);
850         itr->obj = obj;
851     itr->done = 0;
852     itr->current = NULL;
853         return itr;
854 }
855
856 jsonObjectNode* jsonObjectIteratorNext( jsonObjectIterator* itr ) {
857         if(itr == NULL || itr->done) return NULL;
858
859     if(itr->current) free(itr->current);
860     jsonObject* next = jsonIteratorNext(itr->iterator);
861     if(next == NULL) {
862         itr->current = NULL;
863         itr->done = 1;
864         return NULL;
865     }
866     itr->current = makeNode(next, itr->iterator->index, itr->iterator->key);
867     return itr->current;
868 }
869
870 void jsonObjectIteratorFree(jsonObjectIterator* iter) { 
871     if(iter->current) free(iter->current);
872     jsonIteratorFree(iter->iterator);
873         free(iter);
874 }
875
876 int jsonObjectIteratorHasNext(const jsonObjectIterator* itr) {
877         return (itr && itr->current);
878 }
879
880