1 /***********************************************************************************************************************************
2 Convert JSON to/from KeyValue
3 ***********************************************************************************************************************************/
4 #include "build.auto.h"
5
6 #include <ctype.h>
7 #include <string.h>
8
9 #include "common/debug.h"
10 #include "common/log.h"
11 #include "common/type/json.h"
12
13 /***********************************************************************************************************************************
14 Prototypes
15 ***********************************************************************************************************************************/
16 static Variant *jsonToVarInternal(const char *json, unsigned int *jsonPos);
17
18 /***********************************************************************************************************************************
19 Consume whitespace
20 ***********************************************************************************************************************************/
21 static void
jsonConsumeWhiteSpace(const char * json,unsigned int * jsonPos)22 jsonConsumeWhiteSpace(const char *json, unsigned int *jsonPos)
23 {
24 FUNCTION_TEST_BEGIN();
25 FUNCTION_TEST_PARAM(STRINGZ, json);
26 FUNCTION_TEST_PARAM_P(UINT, jsonPos);
27 FUNCTION_TEST_END();
28
29 // Consume whitespace
30 while (json[*jsonPos] == ' ' || json[*jsonPos] == '\t' || json[*jsonPos] == '\n' || json[*jsonPos] == '\r')
31 (*jsonPos)++;
32
33 FUNCTION_TEST_RETURN_VOID();
34 }
35
36 /**********************************************************************************************************************************/
37 static bool
jsonToBoolInternal(const char * json,unsigned int * jsonPos)38 jsonToBoolInternal(const char *json, unsigned int *jsonPos)
39 {
40 FUNCTION_TEST_BEGIN();
41 FUNCTION_TEST_PARAM(STRINGZ, json);
42 FUNCTION_TEST_PARAM_P(UINT, jsonPos);
43 FUNCTION_TEST_END();
44
45 bool result;
46
47 if (strncmp(json + *jsonPos, TRUE_Z, 4) == 0)
48 {
49 result = true;
50 *jsonPos += 4;
51 }
52 else if (strncmp(json + *jsonPos, FALSE_Z, 5) == 0)
53 {
54 result = false;
55 *jsonPos += 5;
56 }
57 else
58 THROW_FMT(JsonFormatError, "expected boolean at '%s'", json + *jsonPos);
59
60 FUNCTION_TEST_RETURN(result);
61 }
62
63 bool
jsonToBool(const String * json)64 jsonToBool(const String *json)
65 {
66 FUNCTION_TEST_BEGIN();
67 FUNCTION_TEST_PARAM(STRING, json);
68 FUNCTION_TEST_END();
69
70 unsigned int jsonPos = 0;
71 jsonConsumeWhiteSpace(strZ(json), &jsonPos);
72
73 bool result = jsonToBoolInternal(strZ(json), &jsonPos);
74
75 jsonConsumeWhiteSpace(strZ(json), &jsonPos);
76
77 if (jsonPos != strSize(json))
78 THROW_FMT(JsonFormatError, "unexpected characters after boolean at '%s'", strZ(json) + jsonPos);
79
80 FUNCTION_TEST_RETURN(result);
81 }
82
83 /**********************************************************************************************************************************/
84 static Variant *
jsonToNumberInternal(const char * json,unsigned int * jsonPos)85 jsonToNumberInternal(const char *json, unsigned int *jsonPos)
86 {
87 FUNCTION_TEST_BEGIN();
88 FUNCTION_TEST_PARAM(STRINGZ, json);
89 FUNCTION_TEST_PARAM_P(UINT, jsonPos);
90 FUNCTION_TEST_END();
91
92 Variant *result = NULL;
93 unsigned int beginPos = *jsonPos;
94 bool intSigned = false;
95
96 // Consume the -
97 if (json[*jsonPos] == '-')
98 {
99 (*jsonPos)++;
100 intSigned = true;
101 }
102
103 // Consume all digits
104 while (isdigit(json[*jsonPos]))
105 (*jsonPos)++;
106
107 // Invalid if only a - was found
108 if (json[*jsonPos - 1] == '-')
109 THROW_FMT(JsonFormatError, "found '-' with no integer at '%s'", json + beginPos);
110
111 MEM_CONTEXT_TEMP_BEGIN()
112 {
113 // Extract the numeric as a string
114 String *resultStr = strNewN(json + beginPos, *jsonPos - beginPos);
115
116 // Convert the string to a integer variant
117 MEM_CONTEXT_PRIOR_BEGIN()
118 {
119 if (intSigned)
120 result = varNewInt64(cvtZToInt64(strZ(resultStr)));
121 else
122 result = varNewUInt64(cvtZToUInt64(strZ(resultStr)));
123 }
124 MEM_CONTEXT_PRIOR_END();
125 }
126 MEM_CONTEXT_TEMP_END();
127
128 FUNCTION_TEST_RETURN(result);
129 }
130
131 static Variant *
jsonToNumber(const String * json)132 jsonToNumber(const String *json)
133 {
134 FUNCTION_TEST_BEGIN();
135 FUNCTION_TEST_PARAM(STRING, json);
136 FUNCTION_TEST_END();
137
138 unsigned int jsonPos = 0;
139 jsonConsumeWhiteSpace(strZ(json), &jsonPos);
140
141 Variant *result = jsonToNumberInternal(strZ(json), &jsonPos);
142
143 jsonConsumeWhiteSpace(strZ(json), &jsonPos);
144
145 if (jsonPos != strSize(json))
146 THROW_FMT(JsonFormatError, "unexpected characters after number at '%s'", strZ(json) + jsonPos);
147
148 FUNCTION_TEST_RETURN(result);
149 }
150
151 int
jsonToInt(const String * json)152 jsonToInt(const String *json)
153 {
154 FUNCTION_TEST_BEGIN();
155 FUNCTION_TEST_PARAM(STRING, json);
156 FUNCTION_TEST_END();
157
158 int result = 0;
159
160 MEM_CONTEXT_TEMP_BEGIN()
161 {
162 result = varIntForce(jsonToNumber(json));
163 }
164 MEM_CONTEXT_TEMP_END();
165
166 FUNCTION_TEST_RETURN(result);
167 }
168
169 int64_t
jsonToInt64(const String * json)170 jsonToInt64(const String *json)
171 {
172 FUNCTION_TEST_BEGIN();
173 FUNCTION_TEST_PARAM(STRING, json);
174 FUNCTION_TEST_END();
175
176 int64_t result = 0;
177
178 MEM_CONTEXT_TEMP_BEGIN()
179 {
180 result = varInt64Force(jsonToNumber(json));
181 }
182 MEM_CONTEXT_TEMP_END();
183
184 FUNCTION_TEST_RETURN(result);
185 }
186
187 unsigned int
jsonToUInt(const String * json)188 jsonToUInt(const String *json)
189 {
190 FUNCTION_TEST_BEGIN();
191 FUNCTION_TEST_PARAM(STRING, json);
192 FUNCTION_TEST_END();
193
194 unsigned int result = 0;
195
196 MEM_CONTEXT_TEMP_BEGIN()
197 {
198 result = varUIntForce(jsonToNumber(json));
199 }
200 MEM_CONTEXT_TEMP_END();
201
202 FUNCTION_TEST_RETURN(result);
203 }
204
205 uint64_t
jsonToUInt64(const String * json)206 jsonToUInt64(const String *json)
207 {
208 FUNCTION_TEST_BEGIN();
209 FUNCTION_TEST_PARAM(STRING, json);
210 FUNCTION_TEST_END();
211
212 uint64_t result = 0;
213
214 MEM_CONTEXT_TEMP_BEGIN()
215 {
216 result = varUInt64Force(jsonToNumber(json));
217 }
218 MEM_CONTEXT_TEMP_END();
219
220 FUNCTION_TEST_RETURN(result);
221 }
222
223 /**********************************************************************************************************************************/
224 static String *
jsonToStrInternal(const char * json,unsigned int * jsonPos)225 jsonToStrInternal(const char *json, unsigned int *jsonPos)
226 {
227 FUNCTION_TEST_BEGIN();
228 FUNCTION_TEST_PARAM(STRINGZ, json);
229 FUNCTION_TEST_PARAM_P(UINT, jsonPos);
230 FUNCTION_TEST_END();
231
232 String *result = strNew();
233
234 if (json[*jsonPos] != '"')
235 THROW_FMT(JsonFormatError, "expected '\"' at '%s'", json + *jsonPos);
236
237 MEM_CONTEXT_TEMP_BEGIN()
238 {
239 // Skip the beginning "
240 (*jsonPos)++;
241
242 // Track portion of string with no escapes
243 const char *noEscape = NULL;
244 size_t noEscapeSize = 0;
245
246 while (json[*jsonPos] != '"')
247 {
248 if (json[*jsonPos] == '\\')
249 {
250 // Copy portion of string without escapes
251 if (noEscapeSize > 0)
252 {
253 strCatZN(result, noEscape, noEscapeSize);
254 noEscapeSize = 0;
255 }
256
257 (*jsonPos)++;
258
259 switch (json[*jsonPos])
260 {
261 case '"':
262 strCatChr(result, '"');
263 break;
264
265 case '\\':
266 strCatChr(result, '\\');
267 break;
268
269 case '/':
270 strCatChr(result, '/');
271 break;
272
273 case 'n':
274 strCatChr(result, '\n');
275 break;
276
277 case 'r':
278 strCatChr(result, '\r');
279 break;
280
281 case 't':
282 strCatChr(result, '\t');
283 break;
284
285 case 'b':
286 strCatChr(result, '\b');
287 break;
288
289 case 'f':
290 strCatChr(result, '\f');
291 break;
292
293 case 'u':
294 {
295 (*jsonPos)++;
296
297 // We don't know how to decode anything except ASCII so fail if it looks like Unicode
298 if (strncmp(json + *jsonPos, "00", 2) != 0)
299 THROW_FMT(JsonFormatError, "unable to decode '%.4s'", json + *jsonPos);
300
301 // Decode char
302 (*jsonPos) += 2;
303 strCatChr(result, (char)cvtZToUIntBase(strZ(strNewN(json + *jsonPos, 2)), 16));
304 (*jsonPos) += 1;
305
306 break;
307 }
308
309 default:
310 THROW_FMT(JsonFormatError, "invalid escape character '%c'", json[*jsonPos]);
311 }
312 }
313 else
314 {
315 if (json[*jsonPos] == '\0')
316 THROW(JsonFormatError, "expected '\"' but found null delimiter");
317
318 // If escape string is zero size then start it
319 if (noEscapeSize == 0)
320 noEscape = json + *jsonPos;
321
322 noEscapeSize++;
323 }
324
325 (*jsonPos)++;;
326 };
327
328 // Copy portion of string without escapes
329 if (noEscapeSize > 0)
330 strCatZN(result, noEscape, noEscapeSize);
331
332 // Advance the character array pointer to the next element after the string
333 (*jsonPos)++;;
334 }
335 MEM_CONTEXT_TEMP_END();
336
337 FUNCTION_TEST_RETURN(result);
338 }
339
340 String *
jsonToStr(const String * json)341 jsonToStr(const String *json)
342 {
343 FUNCTION_TEST_BEGIN();
344 FUNCTION_TEST_PARAM(STRING, json);
345 FUNCTION_TEST_END();
346
347 unsigned int jsonPos = 0;
348 jsonConsumeWhiteSpace(strZ(json), &jsonPos);
349
350 String *result = NULL;
351
352 if (strncmp(strZ(json), NULL_Z, 4) == 0)
353 jsonPos += 4;
354 else
355 result = jsonToStrInternal(strZ(json), &jsonPos);
356
357 jsonConsumeWhiteSpace(strZ(json), &jsonPos);
358
359 if (jsonPos != strSize(json))
360 THROW_FMT(JsonFormatError, "unexpected characters after string at '%s'", strZ(json) + jsonPos);
361
362 FUNCTION_TEST_RETURN(result);
363 }
364
365 /**********************************************************************************************************************************/
366 static KeyValue *
jsonToKvInternal(const char * json,unsigned int * jsonPos)367 jsonToKvInternal(const char *json, unsigned int *jsonPos)
368 {
369 FUNCTION_TEST_BEGIN();
370 FUNCTION_TEST_PARAM(STRINGZ, json);
371 FUNCTION_TEST_PARAM_P(UINT, jsonPos);
372 FUNCTION_TEST_END();
373
374 KeyValue *result = kvNew();
375
376 MEM_CONTEXT_TEMP_BEGIN()
377 {
378 // Move position to the first key/value in the object
379 (*jsonPos)++;
380 jsonConsumeWhiteSpace(json, jsonPos);
381
382 // Only proceed if the array is not empty
383 if (json[*jsonPos] != '}')
384 {
385 do
386 {
387 if (json[*jsonPos] == ',')
388 {
389 (*jsonPos)++;
390 jsonConsumeWhiteSpace(json, jsonPos);
391 }
392
393 Variant *key = varNewStr(jsonToStrInternal(json, jsonPos));
394
395 jsonConsumeWhiteSpace(json, jsonPos);
396
397 if (json[*jsonPos] != ':')
398 THROW_FMT(JsonFormatError, "expected ':' at '%s'", json + *jsonPos);
399 (*jsonPos)++;
400
401 jsonConsumeWhiteSpace(json, jsonPos);
402
403 kvPut(result, key, jsonToVarInternal(json, jsonPos));
404 }
405 while (json[*jsonPos] == ',');
406 }
407
408 if (json[*jsonPos] != '}')
409 THROW_FMT(JsonFormatError, "expected '}' at '%s'", json + *jsonPos);
410 (*jsonPos)++;
411 }
412 MEM_CONTEXT_TEMP_END();
413
414 FUNCTION_TEST_RETURN(result);
415 }
416
417 KeyValue *
jsonToKv(const String * json)418 jsonToKv(const String *json)
419 {
420 FUNCTION_TEST_BEGIN();
421 FUNCTION_TEST_PARAM(STRING, json);
422 FUNCTION_TEST_END();
423
424 unsigned int jsonPos = 0;
425 jsonConsumeWhiteSpace(strZ(json), &jsonPos);
426
427 if (strZ(json)[jsonPos] != '{')
428 THROW_FMT(JsonFormatError, "expected '{' at '%s'", strZ(json) + jsonPos);
429
430 KeyValue *result = jsonToKvInternal(strZ(json), &jsonPos);
431
432 jsonConsumeWhiteSpace(strZ(json), &jsonPos);
433
434 if (jsonPos != strSize(json))
435 THROW_FMT(JsonFormatError, "unexpected characters after object at '%s'", strZ(json) + jsonPos);
436
437 FUNCTION_TEST_RETURN(result);
438 }
439
440 /**********************************************************************************************************************************/
441 static VariantList *
jsonToVarLstInternal(const char * json,unsigned int * jsonPos)442 jsonToVarLstInternal(const char *json, unsigned int *jsonPos)
443 {
444 FUNCTION_TEST_BEGIN();
445 FUNCTION_TEST_PARAM(STRINGZ, json);
446 FUNCTION_TEST_PARAM_P(UINT, jsonPos);
447 FUNCTION_TEST_END();
448
449 VariantList *result = varLstNew();
450
451 if (json[*jsonPos] != '[')
452 THROW_FMT(JsonFormatError, "expected '[' at '%s'", json + *jsonPos);
453
454 MEM_CONTEXT_TEMP_BEGIN()
455 {
456 // Move position to the first element in the array
457 (*jsonPos)++;
458 jsonConsumeWhiteSpace(json, jsonPos);
459
460 // Only proceed if the array is not empty
461 if (json[*jsonPos] != ']')
462 {
463 do
464 {
465 if (json[*jsonPos] == ',')
466 {
467 (*jsonPos)++;
468 jsonConsumeWhiteSpace(json, jsonPos);
469 }
470
471 MEM_CONTEXT_PRIOR_BEGIN()
472 {
473 varLstAdd(result, jsonToVarInternal(json, jsonPos));
474 }
475 MEM_CONTEXT_PRIOR_END();
476
477 jsonConsumeWhiteSpace(json, jsonPos);
478 }
479 while (json[*jsonPos] == ',');
480 }
481
482 if (json[*jsonPos] != ']')
483 THROW_FMT(JsonFormatError, "expected ']' at '%s'", json + *jsonPos);
484
485 (*jsonPos)++;
486 }
487 MEM_CONTEXT_TEMP_END();
488
489 FUNCTION_TEST_RETURN(result);
490 }
491
492 VariantList *
jsonToVarLst(const String * json)493 jsonToVarLst(const String *json)
494 {
495 FUNCTION_TEST_BEGIN();
496 FUNCTION_TEST_PARAM(STRING, json);
497 FUNCTION_TEST_END();
498
499 unsigned int jsonPos = 0;
500 jsonConsumeWhiteSpace(strZ(json), &jsonPos);
501
502 VariantList *result = jsonToVarLstInternal(strZ(json), &jsonPos);
503
504 jsonConsumeWhiteSpace(strZ(json), &jsonPos);
505
506 if (jsonPos != strSize(json))
507 THROW_FMT(JsonFormatError, "unexpected characters after array at '%s'", strZ(json) + jsonPos);
508
509 FUNCTION_TEST_RETURN(result);
510 }
511
512 /**********************************************************************************************************************************/
513 static Variant *
jsonToVarInternal(const char * json,unsigned int * jsonPos)514 jsonToVarInternal(const char *json, unsigned int *jsonPos)
515 {
516 FUNCTION_TEST_BEGIN();
517 FUNCTION_TEST_PARAM(STRINGZ, json);
518 FUNCTION_TEST_PARAM_P(UINT, jsonPos);
519 FUNCTION_TEST_END();
520
521 Variant *result = NULL;
522
523 jsonConsumeWhiteSpace(json, jsonPos);
524
525 // There should be some data
526 if (json[*jsonPos] == '\0')
527 THROW(JsonFormatError, "expected data");
528
529 // Determine data type
530 switch (json[*jsonPos])
531 {
532 // String
533 case '"':
534 result = varNewStr(jsonToStrInternal(json, jsonPos));
535 break;
536
537 // Integer
538 case '-':
539 case '0' ... '9':
540 result = jsonToNumberInternal(json, jsonPos);
541 break;
542
543 // Boolean
544 case 't':
545 case 'f':
546 result = varNewBool(jsonToBoolInternal(json, jsonPos));
547 break;
548
549 // Null
550 case 'n':
551 {
552 if (strncmp(json + *jsonPos, NULL_Z, 4) == 0)
553 *jsonPos += 4;
554 else
555 THROW_FMT(JsonFormatError, "expected null at '%s'", json + *jsonPos);
556
557 break;
558 }
559
560 // Array
561 case '[':
562 result = varNewVarLst(jsonToVarLstInternal(json, jsonPos));
563 break;
564
565 // Object
566 case '{':
567 result = varNewKv(jsonToKvInternal(json, jsonPos));
568 break;
569
570 // Object
571 default:
572 THROW_FMT(JsonFormatError, "invalid type at '%s'", json + *jsonPos);
573 break;
574 }
575
576 jsonConsumeWhiteSpace(json, jsonPos);
577
578 FUNCTION_TEST_RETURN(result);
579 }
580
581 Variant *
jsonToVar(const String * json)582 jsonToVar(const String *json)
583 {
584 FUNCTION_LOG_BEGIN(logLevelTrace);
585 FUNCTION_LOG_PARAM(STRING, json);
586 FUNCTION_LOG_END();
587
588 const char *jsonPtr = strZ(json);
589 unsigned int jsonPos = 0;
590
591 Variant *result = jsonToVarInternal(jsonPtr, &jsonPos);
592
593 if (jsonPos != strSize(json))
594 THROW_FMT(JsonFormatError, "unexpected characters after JSON at '%s'", strZ(json) + jsonPos);
595
596 FUNCTION_LOG_RETURN(VARIANT, result);
597 }
598
599 /**********************************************************************************************************************************/
600 const String *
jsonFromBool(bool value)601 jsonFromBool(bool value)
602 {
603 FUNCTION_TEST_BEGIN();
604 FUNCTION_TEST_PARAM(BOOL, value);
605 FUNCTION_TEST_END();
606
607 FUNCTION_TEST_RETURN(value ? TRUE_STR : FALSE_STR);
608 }
609
610 /**********************************************************************************************************************************/
611 String *
jsonFromInt(int number)612 jsonFromInt(int number)
613 {
614 FUNCTION_TEST_BEGIN();
615 FUNCTION_TEST_PARAM(INT, number);
616 FUNCTION_TEST_END();
617
618 char working[CVT_BASE10_BUFFER_SIZE];
619 cvtIntToZ(number, working, sizeof(working));
620
621 FUNCTION_TEST_RETURN(strNewZ(working));
622 }
623
624 String *
jsonFromInt64(int64_t number)625 jsonFromInt64(int64_t number)
626 {
627 FUNCTION_TEST_BEGIN();
628 FUNCTION_TEST_PARAM(INT64, number);
629 FUNCTION_TEST_END();
630
631 char working[CVT_BASE10_BUFFER_SIZE];
632 cvtInt64ToZ(number, working, sizeof(working));
633
634 FUNCTION_TEST_RETURN(strNewZ(working));
635 }
636
637 String *
jsonFromUInt(unsigned int number)638 jsonFromUInt(unsigned int number)
639 {
640 FUNCTION_TEST_BEGIN();
641 FUNCTION_TEST_PARAM(UINT, number);
642 FUNCTION_TEST_END();
643
644 char working[CVT_BASE10_BUFFER_SIZE];
645 cvtUIntToZ(number, working, sizeof(working));
646
647 FUNCTION_TEST_RETURN(strNewZ(working));
648 }
649
650 String *
jsonFromUInt64(uint64_t number)651 jsonFromUInt64(uint64_t number)
652 {
653 FUNCTION_TEST_BEGIN();
654 FUNCTION_TEST_PARAM(UINT64, number);
655 FUNCTION_TEST_END();
656
657 char working[CVT_BASE10_BUFFER_SIZE];
658 cvtUInt64ToZ(number, working, sizeof(working));
659
660 FUNCTION_TEST_RETURN(strNewZ(working));
661 }
662
663 /**********************************************************************************************************************************/
664 static void
jsonFromStrInternal(String * json,const String * string)665 jsonFromStrInternal(String *json, const String *string)
666 {
667 FUNCTION_TEST_BEGIN();
668 FUNCTION_TEST_PARAM(STRING, json);
669 FUNCTION_TEST_PARAM(STRING, string);
670 FUNCTION_TEST_END();
671
672 ASSERT(json != NULL);
673
674 // If string is null
675 if (string == NULL)
676 {
677 strCat(json, NULL_STR);
678 }
679 // Else escape and output string
680 else
681 {
682 strCatChr(json, '"');
683
684 // Track portion of string with no escapes
685 const char *noEscape = NULL;
686 size_t noEscapeSize = 0;
687
688 for (unsigned int stringIdx = 0; stringIdx < strSize(string); stringIdx++)
689 {
690 char stringChr = strZ(string)[stringIdx];
691
692 switch (stringChr)
693 {
694 case '"':
695 case '\\':
696 case '\n':
697 case '\r':
698 case '\t':
699 case '\b':
700 case '\f':
701 {
702 // Copy portion of string without escapes
703 if (noEscapeSize > 0)
704 {
705 strCatZN(json, noEscape, noEscapeSize);
706 noEscapeSize = 0;
707 }
708
709 switch (stringChr)
710 {
711 case '"':
712 strCatZ(json, "\\\"");
713 break;
714
715 case '\\':
716 strCatZ(json, "\\\\");
717 break;
718
719 case '\n':
720 strCatZ(json, "\\n");
721 break;
722
723 case '\r':
724 strCatZ(json, "\\r");
725 break;
726
727 case '\t':
728 strCatZ(json, "\\t");
729 break;
730
731 case '\b':
732 strCatZ(json, "\\b");
733 break;
734
735 case '\f':
736 strCatZ(json, "\\f");
737 break;
738 }
739
740 break;
741 }
742
743 default:
744 {
745 // If escape string is zero size then start it
746 if (noEscapeSize == 0)
747 noEscape = strZ(string) + stringIdx;
748
749 noEscapeSize++;
750 break;
751 }
752 }
753 }
754
755 // Copy portion of string without escapes
756 if (noEscapeSize > 0)
757 strCatZN(json, noEscape, noEscapeSize);
758
759 strCatChr(json, '"');
760 }
761
762 FUNCTION_TEST_RETURN_VOID();
763 }
764
765 String *
jsonFromStr(const String * string)766 jsonFromStr(const String *string)
767 {
768 FUNCTION_TEST_BEGIN();
769 FUNCTION_TEST_PARAM(STRING, string);
770 FUNCTION_TEST_END();
771
772 String *json = strNew();
773 jsonFromStrInternal(json, string);
774
775 FUNCTION_TEST_RETURN(json);
776 }
777
778 /***********************************************************************************************************************************
779 Internal recursive function to walk a KeyValue and return a json string
780 ***********************************************************************************************************************************/
781 static String *
jsonFromKvInternal(const KeyValue * kv)782 jsonFromKvInternal(const KeyValue *kv)
783 {
784 FUNCTION_TEST_BEGIN();
785 FUNCTION_TEST_PARAM(KEY_VALUE, kv);
786 FUNCTION_TEST_END();
787
788 ASSERT(kv != NULL);
789
790 String *result = strNewZ("{");
791
792 MEM_CONTEXT_TEMP_BEGIN()
793 {
794 const StringList *keyList = strLstSort(strLstNewVarLst(kvKeyList(kv)), sortOrderAsc);
795
796 for (unsigned int keyIdx = 0; keyIdx < strLstSize(keyList); keyIdx++)
797 {
798 String *key = strLstGet(keyList, keyIdx);
799 const Variant *value = kvGet(kv, VARSTR(key));
800
801 // If going to add another key, prepend a comma
802 if (keyIdx > 0)
803 strCatZ(result, ",");
804
805 // Keys are always strings in the output, so add starting quote and colon.
806 strCatFmt(result, "\"%s\":", strZ(key));
807
808 // NULL value
809 if (value == NULL)
810 strCat(result, NULL_STR);
811 else
812 {
813 switch (varType(value))
814 {
815 case varTypeKeyValue:
816 strCat(result, jsonFromKvInternal(kvDup(varKv(value))));
817 break;
818
819 case varTypeVariantList:
820 {
821 // If the array is empty, then do not add formatting, else process the array.
822 if (varVarLst(value) == NULL)
823 strCat(result, NULL_STR);
824 else if (varLstEmpty(varVarLst(value)))
825 strCatZ(result, "[]");
826 else
827 {
828 strCatZ(result, "[");
829
830 for (unsigned int arrayIdx = 0; arrayIdx < varLstSize(varVarLst(value)); arrayIdx++)
831 {
832 Variant *arrayValue = varLstGet(varVarLst(value), arrayIdx);
833
834 // If going to add another element, add a comma
835 if (arrayIdx > 0)
836 strCatZ(result, ",");
837
838 // If array value is null
839 if (arrayValue == NULL)
840 {
841 strCat(result, NULL_STR);
842 }
843 // If the type is a string, add leading and trailing double quotes
844 else if (varType(arrayValue) == varTypeString)
845 {
846 jsonFromStrInternal(result, varStr(arrayValue));
847 }
848 else if (varType(arrayValue) == varTypeKeyValue)
849 {
850 strCat(result, jsonFromKvInternal(kvDup(varKv(arrayValue))));
851 }
852 else if (varType(arrayValue) == varTypeVariantList)
853 {
854 strCat(result, jsonFromVar(arrayValue));
855 }
856 // Numeric, Boolean or other type
857 else
858 strCat(result, varStrForce(arrayValue));
859 }
860
861 strCatZ(result, "]");
862 }
863
864 break;
865 }
866
867 // String
868 case varTypeString:
869 jsonFromStrInternal(result, varStr(value));
870 break;
871
872 default:
873 strCat(result, varStrForce(value));
874 break;
875 }
876 }
877 }
878
879 result = strCatZ(result, "}");
880 }
881 MEM_CONTEXT_TEMP_END();
882
883 FUNCTION_TEST_RETURN(result);
884 }
885
886 /***********************************************************************************************************************************
887 Currently this function is only intended to convert the limited types that are included in info files. More types will be added as
888 needed. Since this function is only intended to read internally-generated JSON it is assumed to be well-formed with no extraneous
889 whitespace.
890 ***********************************************************************************************************************************/
891 String *
jsonFromKv(const KeyValue * kv)892 jsonFromKv(const KeyValue *kv)
893 {
894 FUNCTION_LOG_BEGIN(logLevelTrace);
895 FUNCTION_LOG_PARAM(KEY_VALUE, kv);
896 FUNCTION_LOG_END();
897
898 ASSERT(kv != NULL);
899
900 String *result = NULL;
901
902 MEM_CONTEXT_TEMP_BEGIN()
903 {
904 String *jsonStr = jsonFromKvInternal(kv);
905
906 // Duplicate the string into the prior context
907 MEM_CONTEXT_PRIOR_BEGIN()
908 {
909 result = strDup(jsonStr);
910 }
911 MEM_CONTEXT_PRIOR_END();
912 }
913 MEM_CONTEXT_TEMP_END();
914
915 FUNCTION_LOG_RETURN(STRING, result);
916 }
917
918 /***********************************************************************************************************************************
919 Currently this function is only intended to convert the limited types that are included in info files. More types will be added as
920 needed.
921 ***********************************************************************************************************************************/
922 String *
jsonFromVar(const Variant * var)923 jsonFromVar(const Variant *var)
924 {
925 FUNCTION_LOG_BEGIN(logLevelTrace);
926 FUNCTION_LOG_PARAM(VARIANT, var);
927 FUNCTION_LOG_END();
928
929 String *result = NULL;
930
931 MEM_CONTEXT_TEMP_BEGIN()
932 {
933 String *jsonStr = strNew();
934
935 // If VariantList then process each item in the array. Currently the list must be KeyValue types.
936 if (var == NULL)
937 {
938 strCat(jsonStr, NULL_STR);
939 }
940 else if (varType(var) == varTypeBool)
941 {
942 strCat(jsonStr, jsonFromBool(varBool(var)));
943 }
944 else if (varType(var) == varTypeUInt)
945 {
946 strCat(jsonStr, jsonFromUInt(varUInt(var)));
947 }
948 else if (varType(var) == varTypeUInt64)
949 {
950 strCat(jsonStr, jsonFromUInt64(varUInt64(var)));
951 }
952 else if (varType(var) == varTypeString)
953 {
954 jsonFromStrInternal(jsonStr, varStr(var));
955 }
956 else if (varType(var) == varTypeVariantList)
957 {
958 const VariantList *vl = varVarLst(var);
959
960 // If null
961 if (vl == NULL)
962 {
963 strCat(jsonStr, NULL_STR);
964 }
965 // Else if not an empty array
966 else if (!varLstEmpty(vl))
967 {
968 strCatZ(jsonStr, "[");
969
970 // Currently only KeyValue and String lists are supported
971 for (unsigned int vlIdx = 0; vlIdx < varLstSize(vl); vlIdx++)
972 {
973 // If going to add another key, append a comma
974 if (vlIdx > 0)
975 strCatZ(jsonStr, ",");
976
977 Variant *varSub = varLstGet(vl, vlIdx);
978
979 if (varSub == NULL)
980 {
981 strCat(jsonStr, NULL_STR);
982 }
983 else if (varType(varSub) == varTypeBool)
984 {
985 strCat(jsonStr, jsonFromBool(varBool(varSub)));
986 }
987 else if (varType(varSub) == varTypeKeyValue)
988 {
989 strCat(jsonStr, jsonFromKvInternal(varKv(varSub)));
990 }
991 else if (varType(varSub) == varTypeVariantList)
992 {
993 strCat(jsonStr, jsonFromVar(varSub));
994 }
995 else if (varType(varSub) == varTypeInt)
996 {
997 strCat(jsonStr, jsonFromInt(varInt(varSub)));
998 }
999 else if (varType(varSub) == varTypeInt64)
1000 {
1001 strCat(jsonStr, jsonFromInt64(varInt64(varSub)));
1002 }
1003 else if (varType(varSub) == varTypeUInt)
1004 {
1005 strCat(jsonStr, jsonFromUInt(varUInt(varSub)));
1006 }
1007 else if (varType(varSub) == varTypeUInt64)
1008 {
1009 strCat(jsonStr, jsonFromUInt64(varUInt64(varSub)));
1010 }
1011 else
1012 jsonFromStrInternal(jsonStr, varStr(varSub));
1013 }
1014
1015 // Close the array
1016 strCatZ(jsonStr, "]");
1017 }
1018 // Else empty array
1019 else
1020 strCatZ(jsonStr, "[]");
1021 }
1022 else if (varType(var) == varTypeKeyValue)
1023 {
1024 strCat(jsonStr, jsonFromKvInternal(varKv(var)));
1025 }
1026 else
1027 THROW(JsonFormatError, "variant type is invalid");
1028
1029 // Duplicate the string into the prior context
1030 MEM_CONTEXT_PRIOR_BEGIN()
1031 {
1032 result = strDup(jsonStr);
1033 }
1034 MEM_CONTEXT_PRIOR_END();
1035 }
1036 MEM_CONTEXT_TEMP_END();
1037
1038 FUNCTION_LOG_RETURN(STRING, result);
1039 }
1040