-
Notifications
You must be signed in to change notification settings - Fork 0
/
jsonoutput.c
662 lines (554 loc) · 21.2 KB
/
jsonoutput.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include <ctype.h>
#include <errno.h>
#include <math.h>
#include "jsontools.h"
/*-----------------------------------------------------------------
* Private Helper functions
*----------------------------------------------------------------*/
static JSONError_t writeJSONString(JSONKeyValue_t* pair, char** output, int depth, int* length);
static JSONError_t writeJSONNumber(JSONKeyValue_t* pair, char** output, int depth, int* length);
static JSONError_t writeJSONBoolean(JSONKeyValue_t* pair, char** output, int depth, int* length);
static JSONError_t writeJSONObject(JSONKeyValue_t* pair, char** output, int depth, int* length);
static JSONError_t writeJSONArray(JSONKeyValue_t* pair, char** output, int depth, int* length);
static JSONError_t writeJSONNull(JSONKeyValue_t* pair, char** output, int depth, int* length);
static char* indent(int depth);
/*-------------------------------------------------------------------
* Implement global function
*-----------------------------------------------------------------*/
/**
* Converts a JSON document object to a JSON message string.
*
* @param document - The completed JSON document object that is to be converted
*
* @param output - The string that will contain the contents of the JSON message
*
* @param length - the actual length of the output string, or -1 if the max is too small
*
* @return JSON_SUCCESS if everything converted properly, an error otherwise
*
* @see JSONError_t
*/
JSONError_t documentToString(JSONKeyValue_t* document, char** output, int* length) {
if (document == NULL) {
json_errno = JSON_NULL_ARGUMENT;
return JSON_NULL_ARGUMENT;
}
//We will need to calculate exactly how much room in memeory is need
//to make this pair into a string value.
int valLen = 0;
int otherStuff = 10; //Other string formatting stuff
int strLen = 0;
char* values[document->length];
int index = 0;
JSONError_t status;
JSONKeyValue_t* currentElement = NULL;
if (document->type == OBJECT) {
currentElement = document->value->oVal;
}
else if (document->type == ARRAY){
currentElement = document->value->aVal;
}
else {
return JSON_INVALID_VALUE;
}
int tempLength = 0;
while(currentElement != NULL){
switch (currentElement->type){
case STRING:
status = writeJSONString(currentElement, &values[index++], 1, &tempLength);
break;
case NUMBER:
status = writeJSONNumber(currentElement, &values[index++], 1, &tempLength);
break;
case BOOLEAN:
status = writeJSONBoolean(currentElement, &values[index++], 1, &tempLength);
break;
case ARRAY:
status = writeJSONArray(currentElement, &values[index++], 1, &tempLength);
break;
case OBJECT:
status = writeJSONObject(currentElement, &values[index++], 1, &tempLength);
break;
case NIL:
status = writeJSONNull(currentElement, &values[index++], 1, &tempLength);
break;
default:
json_errno = JSON_INVALID_TYPE;
return JSON_INVALID_TYPE;
}
if (status != JSON_SUCCESS){
json_errno = status;
return status;
}
currentElement = currentElement->next;
}
for (int i = 0; i < document->length; i++) {
valLen += strlen(values[i]);
otherStuff += 3;
}
*output = (char*) malloc(sizeof(char) * (valLen + otherStuff));
if (document->type == OBJECT) {
sprintf(*output, "{\n");
}
else if (document->type == ARRAY){
sprintf(*output, "[\n");
}
for (int i = 0; i < document->length - 1; i++){
strcat(*output, values[i]);
strcat(*output, ",\n");
}
if (document->length > 0){
strcat(*output, values[document->length - 1]);
strcat(*output, "\n");
}
if (document->type == OBJECT){
strcat(*output, "}\n");
}
else if (document->type == ARRAY){
strcat(*output, "]\n");
}
strLen = strlen(*output);
*output = (char*) realloc(*output, strLen + 1);
*length = strLen;
//Free Memory
for (int i = 0; i < document->length; i++){
free(values[i]);
}
return JSON_SUCCESS;
}
/*-------------------------------------------------------------------
* Implement private helper functions
*-----------------------------------------------------------------*/
/**
* This helper function will convert a JSON pair that represents a string
* a JSON message. This message can then be added to a larger message and
* passed to another JSON parser to be read in.
*
* @param pair - A JSON pair that represents a string
* @param output - The key and string value converted into a JSON message
* @param depth - The indentation level of the message
* @param length - The size of the JSON message after it has been converted,
* @return JSON_SUCCESS if the pair was converted into a JSON message, error otherwise
*/
static JSONError_t writeJSONString(JSONKeyValue_t* pair, char** output, int depth, int* length){
if (pair){
if (pair->type != STRING){
json_errno = JSON_INVALID_ARGUMENT;
return JSON_INVALID_ARGUMENT;
}
//We will need to calculate exactly how much room in memeory is need
//to make this pair into a string value.
int keyLen = 0;
int valLen = 0;
int otherStuff = 10 + (2 * depth); //Other string formatting stuff
int strLen = 0;
char* ind = indent(depth); //The indent string (this is dynamic so we need to free it)
if (pair->key && pair->value && pair->value->sVal){
keyLen = strlen(pair->key);
valLen = strlen(pair->value->sVal);
*output = (char*) calloc((keyLen + valLen + otherStuff), sizeof(char));
strLen = sprintf(*output, "%s\"%s\" : \"%s\"", ind, pair->key, pair->value->sVal);
}
else if (pair->value && pair->value->sVal){
valLen = strlen(pair->value->sVal);
*output = (char*) calloc((valLen + otherStuff), sizeof(char));
strLen = sprintf(*output, "%s\"%s\"", ind, pair->value->sVal);
}
else{
json_errno = JSON_NULL_VALUE;
return JSON_NULL_VALUE;
}
free(ind);
*length = strLen;
}
else{
json_errno = JSON_NULL_ARGUMENT;
return JSON_NULL_ARGUMENT;
}
return JSON_SUCCESS;
}
/**
* This helper function will write out a number object as a JSON message.
* If the number has no fractioanl portion, it will be written like an integer.
* If the number has a fractional part, it will be written as a double.
*
* @param pair - A JSON pair that represents a string
* @param output - The key and string value converted into a JSON message
* @param depth - The indentation level of the message
* @param length - The size of the JSON message after it has been converted,
* @return JSON_SUCCESS if the pair was converted into a JSON message, error otherwise
*/
static JSONError_t writeJSONNumber(JSONKeyValue_t* pair, char** output, int depth, int* length){
if (pair){
if (pair->type != NUMBER){
json_errno = JSON_INVALID_ARGUMENT;
return JSON_INVALID_ARGUMENT;
}
if (!pair->value){
json_errno = JSON_NULL_VALUE;
return JSON_NULL_VALUE;
}
//Need to see if the number has a fractional part, if it does
//we print a float, if it does not, we print a integer.
long long rawNum = (long long)pair->value->nVal;
bool hasFractional;
if (fabs(pair->value->nVal) - llabs(rawNum) > 0.0){
hasFractional = true;
}
else {
hasFractional = false;
}
//We will need to calculate exactly how much room in memeory is need
//to make this pair into a string value.
int keyLen = 0;
int valLen = 50; //Too much time to find exactly how many digits the number has, safe at 50
int otherStuff = 10 + (2 * depth); //Other string formatting stuff
int strLen = 0;
char* ind = indent(depth); //The indent string (this is dynamic so we need to free it)
if (pair->key && pair->value){
keyLen = strlen(pair->key);
*output = (char*) calloc((keyLen + valLen + otherStuff), sizeof(char));
if (hasFractional){
strLen = sprintf(*output, "%s\"%s\" : %f", ind, pair->key, pair->value->nVal);
}
else {
strLen = sprintf(*output, "%s\"%s\" : %lld", ind, pair->key, rawNum);
}
}
else if (pair->value){
*output = (char*) calloc((valLen + otherStuff), sizeof(char));
if (hasFractional){
strLen = sprintf(*output, "%s%f", ind, pair->value->nVal);
}
else {
strLen = sprintf(*output, "%s%lld", ind, rawNum);
}
}
else {
json_errno = JSON_NULL_VALUE;
return JSON_NULL_VALUE;
}
if (strLen < 0){
json_errno = JSON_INTERNAL_FAILURE;
return JSON_INTERNAL_FAILURE;
}
*length = strLen;
free(ind);
}
else {
json_errno = JSON_NULL_ARGUMENT;
return JSON_NULL_ARGUMENT;
}
return JSON_SUCCESS;
}
/**
* This helper function will write out a boolean object as a JSON message.
* The value will be an unquoted true or false value.
*
* @param pair - A JSON pair that represents a string
* @param output - The key and string value converted into a JSON message
* @param depth - The indentation level of the message
* @param length - The size of the JSON message after it has been converted,
* @return JSON_SUCCESS if the pair was converted into a JSON message, error otherwise
*/
static JSONError_t writeJSONBoolean(JSONKeyValue_t* pair, char** output, int depth, int* length){
if (pair){
if (pair->type != BOOLEAN){
json_errno = JSON_INVALID_ARGUMENT;
return JSON_INVALID_ARGUMENT;
}
char* t = "true";
char* f = "false";
//We will need to calculate exactly how much room in memeory is need
//to make this pair into a string value.
int keyLen = 0;
int valLen = 0;
int otherStuff = 10 + (2 * depth); //Other string formatting stuff
int strLen = 0;
char* ind = indent(depth); //The indent string (this is dynamic so we need to free it)
if (pair->key && pair->value){
keyLen = strlen(pair->key);
valLen = ((pair->value->bVal) ? strlen(t) : strlen(f));
*output = (char*) calloc((keyLen + valLen + otherStuff), sizeof(char));
strLen = sprintf(*output, "%s\"%s\" : %s", ind, pair->key, ((pair->value->bVal) ? t : f));
}
else if (pair->value){
valLen = ((pair->value->bVal) ? strlen(t) : strlen(f));
*output = (char*) calloc((valLen + otherStuff), sizeof(char));
strLen = sprintf(*output, "%s%s", ind, ((pair->value->bVal) ? t : f));
}
else {
return JSON_NULL_VALUE;
}
*length = strLen;
free(ind);
}
else{
json_errno = JSON_INVALID_ARGUMENT;
return JSON_INVALID_ARGUMENT;
}
return JSON_SUCCESS;
}
/**
* This helper function will write out a JSON object type to a JSON message.
* Since objects contain unordered key:value pairs, and those values can be
* any valid JSON value including other objects, this function will call
* the others recurrsivly if necessary.
*
* @param pair - A JSON pair that represents a string
* @param output - The key and string value converted into a JSON message
* @param depth - The indentation level of the message
* @param length - The size of the JSON message after it has been converted,
* @return JSON_SUCCESS if the pair was converted into a JSON message, error otherwise
*/
static JSONError_t writeJSONObject(JSONKeyValue_t* pair, char** output, int depth, int* length){
if (pair){
if (pair->type != OBJECT) {
json_errno = JSON_INVALID_ARGUMENT;
return JSON_INVALID_ARGUMENT;
}
if (!pair->value || !pair->value->oVal) {
json_errno = JSON_NULL_VALUE;
return JSON_NULL_VALUE;
}
//Find out how many children we have
JSONKeyValue_t* current = pair->value->oVal;
int count = 1;
while(current && current->next) {
count++;
current = current->next;
}
char* values[count]; //hold strings for each child
//We will need to calculate exactly how much room in memeory is need
//to make this pair into a string value.
int keyLen = 0;
int valLen = 0;
int otherStuff = 10 + (4 * depth); //Other string formatting stuff
int strLen = 0;
char* ind = indent(depth); //The indent string (this is dynamic so we need to free it)
if (pair->key) {
keyLen = strlen(pair->key);
}
//Loop through all of the objects children and produce string values for them
int index = 0;
JSONError_t status;
current = pair->value->oVal;
int tempLength = 0;
while(current != NULL) {
switch (current->type) {
case STRING:
status = writeJSONString(current, &values[index++], depth + 1, &tempLength);
break;
case NUMBER:
status = writeJSONNumber(current, &values[index++], depth + 1, &tempLength);
break;
case BOOLEAN:
status = writeJSONBoolean(current, &values[index++], depth + 1, &tempLength);
break;
case ARRAY:
status = writeJSONArray(current, &values[index++], depth + 1, &tempLength);
break;
case OBJECT:
status = writeJSONObject(current, &values[index++], depth + 1, &tempLength);
break;
case NIL:
status = writeJSONNull(current, &values[index++], depth + 1, &tempLength);
break;
default:
json_errno = JSON_INVALID_TYPE;
return JSON_INVALID_TYPE;
}
if (status != JSON_SUCCESS){
return status;
}
current = current->next;
}
//Build string based on parsed values
for (int i = 0; i < count; i++){
valLen += strlen(values[i]);
otherStuff += 3; //null char + comma + newline
}
*output = (char*) calloc((keyLen + valLen + otherStuff), sizeof(char));
if (pair->key){
sprintf(*output, "%s\"%s\" : {\n", ind, pair->key);
}
else {
sprintf(*output, "%s{\n", ind);
}
for (int i = 0; i < count - 1; i++){
strcat(*output, values[i]);
strcat(*output, ",\n");
}
strcat(*output, values[count - 1]);
strcat(*output, "\n");
strcat(*output, ind);
strcat(*output, "}");
strLen = strlen(*output);
*output = (char*) realloc(*output, strLen + 1);
*length = strLen;
//Free Memory
free(ind);
for (int i = 0; i < count; i++){
free(values[i]);
}
}
else{
json_errno = JSON_NULL_ARGUMENT;
return JSON_NULL_ARGUMENT;
}
return JSON_SUCCESS;
}
/**
* This helper function will write out a JSON array as a JSON message. This
* function will call the other helper functions recurrsivly.
*
* @param pair - A JSON pair that represents a string
* @param output - The key and string value converted into a JSON message
* @param depth - The indentation level of the message
* @param length - The size of the JSON message after it has been converted,
* @return JSON_SUCCESS if the pair was converted into a JSON message, error otherwise
*/
static JSONError_t writeJSONArray(JSONKeyValue_t* pair, char** output, int depth, int* length){
if (pair){
if (pair->type != ARRAY){
json_errno = JSON_INVALID_ARGUMENT;
return JSON_INVALID_ARGUMENT;
}
//We will need to calculate exactly how much room in memeory is need
//to make this pair into a string value.
int keyLen = 0;
int valLen = 0;
int otherStuff = 10 + (4 * depth); //Other string formatting stuff
int strLen = 0;
char* ind = indent(depth); //The indent string (this is dynamic so we need to free it)
char* values[pair->length]; //Hold the string values for the elements of the array
if (pair->key){
keyLen = strlen(pair->key);
}
JSONKeyValue_t* current = pair->value->aVal;
JSONError_t status;
int index = 0;
int tempLength = 0;
while(current != NULL){
switch (current->type){
case STRING:
status = writeJSONString(current, &values[index++], depth + 1, &tempLength);
break;
case NUMBER:
status = writeJSONNumber(current, &values[index++], depth + 1, &tempLength);
break;
case BOOLEAN:
status = writeJSONBoolean(current, &values[index++], depth + 1, &tempLength);
break;
case ARRAY:
status = writeJSONArray(current, &values[index++], depth + 1, &tempLength);
break;
case OBJECT:
status = writeJSONObject(current, &values[index++], depth + 1, &tempLength);
break;
case NIL:
status = writeJSONNull(current, &values[index++], depth + 1, &tempLength);
break;
default:
json_errno = JSON_INVALID_TYPE;
return JSON_INVALID_TYPE;
break;
}
if (status != JSON_SUCCESS){
return status;
}
current = current->next;
}
//Build array string
//find how much memory to use
for (int i = 0; i < pair->length; i++){
valLen += (strlen(values[i]));
otherStuff += 3; //null char, comma, newline
}
*output = (char*) calloc((keyLen + valLen + otherStuff), sizeof(char));
if (pair->key){
sprintf(*output, "%s\"%s\" : [\n", ind, pair->key);
}
else {
sprintf(*output, "%s[\n", ind);
}
for (int i = 0; i < pair->length - 1; i++){
strcat(*output, values[i]);
strcat(*output, ",\n");
}
if (pair->length > 0){
strcat(*output, values[pair->length - 1]);
}
strcat(*output, "\n");
strcat(*output, ind);
strcat(*output, "]");
strLen = strlen(*output);
*output = (char*) realloc(*output, strLen + 1);
*length = strLen;
//Free Memory
free(ind);
for (int i = 0; i < pair->length; i++){
free(values[i]);
}
}
else {
json_errno = JSON_NULL_ARGUMENT;
return JSON_NULL_ARGUMENT;
}
return JSON_SUCCESS;
}
/**
* This helper function will write out a JSON null value as a JSON message.
* A JSON null value is simply the unquoted word null, with no key.
*
* @param pair - A JSON pair that represents a string
* @param output - The key and string value converted into a JSON message
* @param depth - The indentation level of the message
* @param length - The size of the JSON message after it has been converted,
* @return JSON_SUCCESS if the pair was converted into a JSON message, error otherwise
*/
static JSONError_t writeJSONNull(JSONKeyValue_t* pair, char** output, int depth, int* length) {
if (pair) {
if (pair->type != NIL) {
json_errno = JSON_INVALID_ARGUMENT;
return JSON_INVALID_ARGUMENT;
}
//We will need to calculate exactly how much room in memeory is need
//to make this pair into a string value.
int valLen = 5 + (pair->key ? strlen(pair->key) : 0); //The word null plus the key
int otherStuff = 3 + (2 * depth); //Other string formatting stuff 3 = 2 for quotes, 1 for colon
int strLen = 0;
char* ind = indent(depth); //The indent string (this is dynamic so we need to free it)
*output = (char*) calloc((valLen + otherStuff), sizeof(char));
if (pair->key){
strLen = sprintf(*output, "%s\"%s\":%s", ind, pair->key, "null");
}
else {
strLen = sprintf(*output, "%s%s", ind, "null");
}
*length = strLen;
free(ind);
}
else {
json_errno = JSON_NULL_ARGUMENT;
return JSON_NULL_ARGUMENT;
}
return JSON_SUCCESS;
}
/**
* This will create the approperate amount of spaces for the indent depth
* of the message being generated. This string is created dynamicly so it
* must be freed when it is no longer needed to avoid memory leaks.
*/
static char* indent(int depth) {
int length = (depth * 2) + 1;
char* output = (char*) calloc(length, sizeof(char));
for (int i = 0; i < depth; i++){
strcat(output, " ");
}
return output;
}