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