flex-json 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,1750 @@
1
+ const FlexJsonConstants = require("./FlexJsonConstants.js");
2
+ const FlexJsonPosition = require("./FlexJsonPosition.js");
3
+ const FlexJsonMeta = require("./FlexJsonMeta.js");
4
+ const fs = require("fs");
5
+ const StringBuilder = require("string-builder");
6
+
7
+ class FlexJson {
8
+ _status = 0;
9
+ _jsonType = "";
10
+ _key = null;
11
+ _value = null;
12
+ _value_valid = false;
13
+ _jsonString = "";
14
+ _jsonString_valid = false;
15
+
16
+ Parent;
17
+ ALLOW_SINGLE_QUOTE_STRINGS = true;
18
+ ENCODE_SINGLE_QUOTES = false;
19
+
20
+ _UseFlexJson = false;
21
+
22
+ _meta = null;
23
+ _NoStatsOrMsgs = false;
24
+
25
+ constructor(InitialJSON, UseFlexJsonFlag) {
26
+ if (UseFlexJsonFlag == true || UseFlexJsonFlag == false) {
27
+ this.UseFlexJson = UseFlexJsonFlag;
28
+ }
29
+ if (InitialJSON) {
30
+ this.Deserialize(InitialJSON + "", 0, false);
31
+ }
32
+ }
33
+
34
+ createMetaIfNeeded(force = false) {
35
+ if (this._NoStatsOrMsgs && !force) {
36
+ return;
37
+ } // Do not track stats/message/meta-data
38
+ if (this._meta == null) {
39
+ this._meta = new FlexJsonMeta();
40
+ }
41
+ }
42
+
43
+ get trackingStats() {
44
+ if (this._meta == null) return false;
45
+ if (this._meta.stats == null) return false;
46
+ return true;
47
+ }
48
+
49
+ get statusMsg() {
50
+ if (this._meta != null) {
51
+ if (this._meta.statusMsg != null) {
52
+ return this._meta.statusMsg;
53
+ }
54
+ }
55
+ return "";
56
+ }
57
+ set statusMsg(value) {
58
+ if (this.NoStatsOrMsgs) {
59
+ return;
60
+ }
61
+ this.createMetaIfNeeded();
62
+ if (this._meta != null) {
63
+ this._meta.statusMsg = value;
64
+ }
65
+ if (this._meta.msgLogOn) {
66
+ if (this._meta != null) {
67
+ this._meta.msgLog.add(value);
68
+ }
69
+ }
70
+ }
71
+
72
+ get tmpStatusMsg() {
73
+ if (this._meta != null) {
74
+ if (this._meta.tmpStatusMsg != null) {
75
+ return this._meta.tmpStatusMsg;
76
+ }
77
+ }
78
+ return "";
79
+ }
80
+ set tmpStatusMsg(value) {
81
+ this.createMetaIfNeeded();
82
+ if (_meta != null) {
83
+ _meta.tmpStatusMsg = value;
84
+ }
85
+ }
86
+
87
+ get key() {
88
+ return this._key;
89
+ }
90
+
91
+ set key(value) {
92
+ this._key = value + "";
93
+ }
94
+
95
+ get preSpace() {
96
+ if (this._meta != null) {
97
+ if (this._meta.preSpace != null) {
98
+ return this._meta.preSpace;
99
+ }
100
+ }
101
+ return null;
102
+ }
103
+
104
+ set preSpace(value) {
105
+ this.createMetaIfNeeded();
106
+ if (this._meta != null) {
107
+ this._meta.preSpace = value;
108
+ }
109
+ }
110
+
111
+ get postSpace() {
112
+ if (this._meta != null) {
113
+ if (this._meta.postSpace != null) {
114
+ return this._meta.postSpace;
115
+ }
116
+ }
117
+ return null;
118
+ }
119
+
120
+ set postSpace(value) {
121
+ this.createMetaIfNeeded();
122
+ if (this._meta != null) {
123
+ this._meta.postSpace = value;
124
+ }
125
+ }
126
+
127
+ get finalSpace() {
128
+ if (this._meta != null) {
129
+ if (this._meta.finalSpace != null) {
130
+ return this._meta.finalSpace;
131
+ }
132
+ }
133
+ return null;
134
+ }
135
+
136
+ set finalSpace(value) {
137
+ this.createMetaIfNeeded();
138
+ if (this._meta != null) {
139
+ this._meta.finalSpace = value;
140
+ }
141
+ }
142
+
143
+ get preKey() {
144
+ if (this._meta != null) {
145
+ if (this._meta.preKey != null) {
146
+ return this._meta.preKey;
147
+ }
148
+ }
149
+ return null;
150
+ }
151
+
152
+ set preKey(value) {
153
+ this.createMetaIfNeeded();
154
+ if (this._meta != null) {
155
+ this._meta.preKey = value;
156
+ }
157
+ }
158
+
159
+ get postKey() {
160
+ if (this._meta != null) {
161
+ if (this._meta.postKey != null) {
162
+ return this._meta.postKey;
163
+ }
164
+ }
165
+ return null;
166
+ }
167
+
168
+ set postKey(value) {
169
+ this.createMetaIfNeeded();
170
+ if (this._meta != null) {
171
+ this._meta.postKey = value;
172
+ }
173
+ }
174
+
175
+ get keepSpacing() {
176
+ if (this._meta != null) {
177
+ return this._meta.keepSpacing;
178
+ }
179
+ return false;
180
+ }
181
+ set keepSpacing(value) {
182
+ this.createMetaIfNeeded();
183
+ if (this._meta != null) {
184
+ this._meta.keepSpacing = value;
185
+ }
186
+ }
187
+
188
+ get keepComments() {
189
+ if (this._meta != null) {
190
+ return this._meta.keepComments;
191
+ }
192
+ return false;
193
+ }
194
+ set keepComments(value) {
195
+ this.createMetaIfNeeded();
196
+ if (this._meta != null) {
197
+ this._meta.keepComments = value;
198
+ }
199
+ }
200
+
201
+ get UseFlexJson() {
202
+ return this._UseFlexJson;
203
+ }
204
+ set UseFlexJson(value) {
205
+ this._UseFlexJson = value;
206
+ this.InvalidateJsonString(1); // If we switch to FlexJson then all our stored JSON strings could be incorrect.
207
+ }
208
+
209
+ get Status() {
210
+ return this._status;
211
+ } // End Property
212
+
213
+ get jsonType() {
214
+ return this._jsonType;
215
+ }
216
+
217
+ get jsonString() {
218
+ // if (trackingStats) { IncStats("stat_jsonString_get"); }
219
+ if (this._status != 0) {
220
+ return "";
221
+ } // *** ERROR - status is invalid
222
+ if (!this._jsonString_valid) {
223
+ if (!this._value_valid) {
224
+ // *** Neither is valid - this JSON object is null
225
+ return "";
226
+ } else {
227
+ // *** First we need to serialize
228
+ this.SerializeMe();
229
+ if (this._status != 0) {
230
+ return "";
231
+ } // *** ERROR - status is invalid
232
+ return this._jsonString;
233
+ }
234
+ } else {
235
+ // jsonString was already valid.
236
+ return this._jsonString;
237
+ }
238
+ } // end getJsonString()
239
+
240
+ set jsonString(value) {
241
+ //if (trackingStats) { IncStats("stat_jsonString_set"); }
242
+ if (
243
+ this._status == 0 &&
244
+ this._jsonString_valid &&
245
+ this._jsonString == value
246
+ ) {
247
+ return;
248
+ } else {
249
+ this.Clear(false, 1); // *** This clears _value AND notifies Parent that we have changed.
250
+ this._jsonString = value;
251
+ this._jsonString_valid = true; // *** This does not indicate 'valid JSON syntax' but only that the string has been populated.
252
+ }
253
+ } //End Set jsonString
254
+
255
+ // Gets Array.Length or Dictionary.Count for array/object. Gets 1 for all other types (string, number, null, etc.) Returns 0 if _value is not valid.
256
+ get length() {
257
+ if (this._status != 0 || !this._value_valid) {
258
+ return 0;
259
+ }
260
+ if (this._jsonType == "null") {
261
+ return 0;
262
+ }
263
+ if (this._jsonType != "array" && this._jsonType != "object") {
264
+ return 1;
265
+ }
266
+ // otherwise we are a _jsonType="object" or _jsonType="array"
267
+ try {
268
+ return this._value.length;
269
+ } catch {
270
+ return -1;
271
+ } //Error occurred... return -1 to indicate an error
272
+ }
273
+ // There is no "set" for length
274
+
275
+ Clear(ClearParent = true, src = 0, bClearStats = false) {
276
+ this._status = 0;
277
+ this._jsonType = "";
278
+ this._value = null;
279
+ this._value_valid = false;
280
+ this.InvalidateJsonString(src); // *** Resets _jsonString and _jsonString_valid (and notifies Parent of changes)
281
+ this.endpos = 0;
282
+ if (ClearParent) {
283
+ this.Parent = null;
284
+ }
285
+ } // end Clear()
286
+
287
+ InvalidateJsonString(src = 0) {
288
+ this._jsonString = "";
289
+ this._jsonString_valid = false;
290
+ // *** if our jsonString is invalid, { the PARENT jsonString would also be invalid
291
+ if (!(this.Parent == null)) {
292
+ this.Parent.InvalidateJsonString(2);
293
+ }
294
+ } // end InvalidateJsonString()
295
+
296
+ keepSpacingAndComments(spacing_flag = -1, comments_flag = -1) {
297
+ if (typeof spacing_flag == "boolean") {
298
+ this.keepSpacing = spacing_flag;
299
+ } else {
300
+ if (spacing_flag == 0) {
301
+ this.keepSpacing = false;
302
+ }
303
+ if (spacing_flag == 1) {
304
+ this.keepSpacing = true;
305
+ }
306
+ }
307
+ if (typeof comments_flag == "boolean") {
308
+ this.keepComments = comments_flag;
309
+ } else {
310
+ if (comments_flag == 0) {
311
+ this.keepComments = false;
312
+ }
313
+ if (comments_flag == 1) {
314
+ this.keepComments = true;
315
+ }
316
+ }
317
+ }
318
+
319
+ ValidateValue() {
320
+ if (this._status != 0) {
321
+ return false;
322
+ } // *** ERROR - status is invalid
323
+ if (!this._value_valid) {
324
+ if (!this._jsonString_valid) {
325
+ // *** Neither is valid - this JSON object is null - Cannot validate value
326
+ return false;
327
+ } else {
328
+ // *** First we need to Deserialize
329
+ DeserializeMe();
330
+ if (this._status != 0) {
331
+ return false;
332
+ } // *** ERROR - status is invalid - failed to Deserialize
333
+ return true;
334
+ }
335
+ } else {
336
+ return true;
337
+ }
338
+ }
339
+
340
+ value(idx, dotNotation = true) {
341
+ return this.v(idx, dotNotation);
342
+ }
343
+
344
+ v(idx, dotNotation = true) {
345
+ //if (trackingStats) { IncStats("stat_Value_get"); } // FUTURE-NEW
346
+ if (!idx) {
347
+ // retun the value of this item
348
+ if (!this.ValidateValue()) {
349
+ return null;
350
+ } // *** Unable to validate/Deserialize the value
351
+ // If object or array, return null
352
+ if (this._jsonType == "object" || this._jsonType == "array") {
353
+ return null;
354
+ } // FUTURE-NEW: return object/array converted into javascript object/array? (indicate iteration level in method parameters)
355
+ return this._value;
356
+ } else {
357
+ return this.i(idx, dotNotation).v();
358
+ }
359
+ return null;
360
+ }
361
+
362
+ get thisValue() {
363
+ return this.v();
364
+ }
365
+ set thisValue(value) {
366
+ // sets the value of the FlexJson object and the jsonType
367
+ this.InvalidateJsonString(); // indicate that the JsonString has changed
368
+ this._status = 0;
369
+ this._jsonType = this.convertType(value);
370
+ if (this._jsonType == "string") {
371
+ this._value = value + ""; // force conversion to string
372
+ this._value_valid = true;
373
+ } else if (this._jsonType) {
374
+ this._value = value;
375
+ this._value_valid = true;
376
+ } else {
377
+ this._jsonType = "error";
378
+ this._value = null;
379
+ this._value_valid = false;
380
+ }
381
+ }
382
+
383
+ toJsonArray(idx, dotNotation = true) {
384
+ //if (trackingStats) { IncStats("stat_Value_get"); } // FUTURE-NEW
385
+ if (!idx) {
386
+ // retun the value of this item
387
+ if (!this.ValidateValue()) {
388
+ return null;
389
+ } // *** Unable to validate/Deserialize the value
390
+ // If object or array, return null
391
+ if (this._jsonType == "object" || this._jsonType == "array") {
392
+ return this._value;
393
+ } // FUTURE-NEW: return object/array converted into javascript object/array? (indicate iteration level in method parameters)
394
+ return null;
395
+ } else {
396
+ return this.i(idx, dotNotation).toJsonArray();
397
+ }
398
+ return null;
399
+ }
400
+
401
+ ConvertToArray() {
402
+ // convert this FlexJson from an Object to an Array
403
+ // We can do this because the only real different between an array and an object is that the object has key values.
404
+ if (this._jsonType != "object") {
405
+ return;
406
+ }
407
+ this._jsonType = "array";
408
+ this.InvalidateJsonString();
409
+ }
410
+
411
+ item(idx, dotNotation = true) {
412
+ return this.i(idx, dotNotation);
413
+ }
414
+ i(idx, dotNotation = true) {
415
+ //if (trackingStats) { IncStats("stat_Item_get"); } // FUTURE-NEW
416
+
417
+ if (Number.isInteger(idx)) {
418
+ if (this.jsonType != "array" && this.jsonType != "object") {
419
+ return FlexJson.CreateNull();
420
+ }
421
+ if (idx < 0 || idx > this.length) {
422
+ return null;
423
+ } // error: we are past the end of the array/object
424
+ return this._value[idx];
425
+ }
426
+
427
+ var searchList;
428
+ let nextItem = this;
429
+ let k = -99; // tells loop not to perform 'next' function
430
+ if (dotNotation) {
431
+ searchList = idx.split(".");
432
+ } else {
433
+ searchList = [idx];
434
+ }
435
+
436
+ for (let sToken of searchList) {
437
+ let sKey = sToken.trim();
438
+ k = -1;
439
+ // Note: missing sKey causes failure to find item FUTURE: should we throw an error?
440
+ if (
441
+ sKey &&
442
+ (nextItem._jsonType == "object" || nextItem._jsonType == "array")
443
+ ) {
444
+ let nKey = null;
445
+ try {
446
+ nCheck = parseInt(sKey);
447
+ if (!isNaN(nCheck)) {
448
+ nKey = nCheck;
449
+ }
450
+ } catch (Exception) {}
451
+ if (nKey) {
452
+ if (nKey < 0 || nKey >= this._value.length) {
453
+ break;
454
+ }
455
+ k = nKey;
456
+ } else {
457
+ if (nextItem._jsonType == "object") {
458
+ k = nextItem.indexOfKey(sKey);
459
+ } else {
460
+ // type array: error because IDX for an array must be an integer
461
+ // FUTURE - throw error here?
462
+ break;
463
+ }
464
+ }
465
+ }
466
+ if (k < 0) {
467
+ break;
468
+ }
469
+ nextItem = nextItem._value[k];
470
+ }
471
+ if (k < 0) {
472
+ return FlexJson.CreateNull();
473
+ } // NOTE! You can check that there is no PARENT on this null object to see that it was NOT-FOUND
474
+
475
+ return nextItem;
476
+ }
477
+
478
+ forEach(callback) {
479
+ if (this.jsonType == "object" || this.jsonType == "array") {
480
+ for (let i = 0; i < this.length; i++) {
481
+ let jj = this.i(i); // debugger
482
+ callback(this.i(i));
483
+ } // end for
484
+ } else {
485
+ callback(this); // if we are not an object/array, then callback one time for this object
486
+ // FUTURE: Should we call back 0 times if we are a NULL or status!=0?
487
+ }
488
+ } // end forEach()
489
+
490
+ // This replaces addToObjBase and addToArrayBase (if array, second argument is not needed)
491
+ addToBase(value, idx = "") {
492
+ this.add(value, idx, false);
493
+ }
494
+
495
+ add(value, idx = "", dotNotation = true) {
496
+ var newV;
497
+ var debugType = typeof value; /// debug debug debug
498
+ if (value === null) {
499
+ /// debug debug debug
500
+ console.log("is null");
501
+ }
502
+ if (
503
+ value !== null &&
504
+ typeof value === "object" &&
505
+ value.constructor.name === "FlexJson"
506
+ ) {
507
+ newV = value; // FUTURE: clone?
508
+ } else {
509
+ newV = new FlexJson();
510
+ newV.thisValue = value; // This also sets the jsonType
511
+ }
512
+ newV.Parent = this;
513
+
514
+ // Can only add to an Object or Array
515
+ if (this._jsonType == "object") {
516
+ let foundItem = this.i(idx, dotNotation);
517
+ if (foundItem.Parent) {
518
+ // Item exists... replace the value
519
+ foundItem.thisValue = value; // this also sets jsonType
520
+ this.InvalidateJsonString(); // indicate that the JsonString has changed.
521
+ } else {
522
+ this.InvalidateJsonString(); // indicate that the JsonString has changed.
523
+ // Item does not exist... we need to add the item (be aware of dotNotation)
524
+ if (dotNotation && idx.indexOf(".") >= 0) {
525
+ // FUTURE: WHAT TO DO HERE!!
526
+ } else {
527
+ newV.key = idx;
528
+ this._value.push(newV);
529
+ //}
530
+ } // else
531
+ } // else
532
+ } else if (this._jsonType == "array") {
533
+ this.InvalidateJsonString(); // indicate that the JsonString has changed.
534
+ // Always push array value onto the end of the array
535
+ newV.key = "";
536
+ this._value.push(newV);
537
+ } else {
538
+ throw "ERROR: add() is only available for FlexJson object or array types.";
539
+ }
540
+ }
541
+
542
+ indexOfKey(Search) {
543
+ if (Search == null) {
544
+ return -1;
545
+ }
546
+ let s = Search.toLowerCase();
547
+ if (!this.ValidateValue()) {
548
+ return -1;
549
+ }
550
+ if (this._jsonType != "object") {
551
+ return -1;
552
+ } // Future: do we want to search for values here? probably not. Implement IndexOf for search of values?
553
+ let k = 0;
554
+ // NOTE: Here we do a linear search. FUTURE: if list is sorted, do a binary search.
555
+ for (let o of this._value) {
556
+ try {
557
+ if (o._key.toLowerCase() == s) {
558
+ return k;
559
+ }
560
+ } catch {}
561
+ k++;
562
+ }
563
+ return -1;
564
+ }
565
+
566
+ contains(idx, dotNotation = true) {
567
+ let foundItem = this.i(idx, dotNotation);
568
+ if (foundItem.Parent) {
569
+ return true;
570
+ } else {
571
+ return false;
572
+ }
573
+ }
574
+
575
+ getStr(idx, defaultValue = "", dotNotation = true) {
576
+ let foundItem = this.i(idx, dotNotation);
577
+ if (foundItem.Parent) {
578
+ return foundItem.toStr(defaultValue);
579
+ } else {
580
+ return defaultValue;
581
+ }
582
+ }
583
+
584
+ getNum(idx, defaultValue = 0, dotNotation = true) {
585
+ // FUTURE-NEW: is there a more efficient way?
586
+ let foundItem = this.i(idx, dotNotation);
587
+ if (foundItem.Parent) {
588
+ return foundItem.toNum(defaultValue);
589
+ } else {
590
+ return defaultValue;
591
+ }
592
+ }
593
+
594
+ getBool(idx, defaultValue = false, dotNotation = true) {
595
+ // FUTURE-NEW: is there a more efficient way?
596
+ let foundItem = this.i(idx, dotNotation);
597
+ if (foundItem.Parent) {
598
+ return foundItem.toBool(defaultValue);
599
+ } else {
600
+ return defaultValue;
601
+ }
602
+ }
603
+
604
+ toStr(defaultValue = "") {
605
+ if (this._status != 0) {
606
+ return defaultValue;
607
+ } // Invalid status
608
+ if (!this._value_valid) {
609
+ return defaultValue;
610
+ }
611
+ if (this._jsonType == FlexJsonConstants.typeString) {
612
+ return this._value;
613
+ }
614
+ if (
615
+ this._jsonType == FlexJsonConstants.typeNull ||
616
+ this._jsonType == FlexJsonConstants.typeObject ||
617
+ this._jsonType == FlexJsonConstants.typeArray
618
+ ) {
619
+ return defaultValue;
620
+ }
621
+ return this._value + "";
622
+ }
623
+
624
+ toNum(defaultValue = "") {
625
+ if (this._status != 0) {
626
+ return defaultValue;
627
+ } // Invalid status
628
+ if (!this._value_valid) {
629
+ return defaultValue;
630
+ }
631
+ if (this._jsonType == FlexJsonConstants.typeNumber) {
632
+ return this._value;
633
+ }
634
+ if (
635
+ this._jsonType == FlexJsonConstants.typeNull ||
636
+ this._jsonType == FlexJsonConstants.typeObject ||
637
+ this._jsonType == FlexJsonConstants.typeArray
638
+ ) {
639
+ return defaultValue;
640
+ }
641
+ return Number(this._value);
642
+ }
643
+
644
+ toBool(defaultValue = "") {
645
+ if (this._status != 0) {
646
+ return defaultValue;
647
+ } // Invalid status
648
+ if (!this._value_valid) {
649
+ return defaultValue;
650
+ }
651
+ if (this._jsonType == FlexJsonConstants.typeBoolean) {
652
+ return this._value;
653
+ }
654
+ if (
655
+ this._jsonType == FlexJsonConstants.typeNull ||
656
+ this._jsonType == FlexJsonConstants.typeObject ||
657
+ this._jsonType == FlexJsonConstants.typeArray
658
+ ) {
659
+ return defaultValue;
660
+ }
661
+ return this.parseBoolean(this._value);
662
+ }
663
+
664
+ parseBoolean(v) {
665
+ return !v || v === "false" || v === "False" || v === "FALSE" ? false : true;
666
+ }
667
+
668
+ static CreateNull() {
669
+ let j = new FlexJson();
670
+ j._value = null;
671
+ j._jsonType = "null";
672
+ j._value_valid = true;
673
+ j._jsonString = "null";
674
+ j._jsonString_valid = true;
675
+ j._status = 0;
676
+ return j;
677
+ }
678
+
679
+ // SerializeMe() - Use this to Serialize the items that are already in the FlexJson object.
680
+ // Return: 0=OK, -1=Error
681
+ SerializeMe() {
682
+ let s = new StringBuilder();
683
+ let i = 0;
684
+ let k = 0;
685
+ let preKey = "";
686
+ let postKey = "";
687
+ //if (this.trackingStats) { IncStats("stat_SerializeMe"); }
688
+ if (this._status != 0) {
689
+ return -1;
690
+ }
691
+ if (!this.ValidateValue()) {
692
+ return -1;
693
+ }
694
+ try {
695
+ let preSpace = this.preSpace;
696
+ if (preSpace) {
697
+ // Here we ignore keepSpacing/keepComments - these flags are only used during the deserialize process
698
+ s.append(preSpace); // preSpace of overall object/array or item
699
+ }
700
+ switch (this._jsonType) {
701
+ case "object":
702
+ s.append("{");
703
+ i = 0;
704
+ this._value.forEach((o) => {
705
+ if (i > 0) {
706
+ s.append(",");
707
+ }
708
+ if (o.key == "data") {
709
+ console.log("debug here111"); // debug debug debug
710
+ }
711
+ let k = o.SerializeMe();
712
+
713
+ // Here we ignore keepSpacing/keepComments - these flags are only used during the deserialize process
714
+ // preSpace and postSpace are already added during the SerializeMe() call above. Here we add preKye and postKey.
715
+ let oPreKey = o.preKey || "";
716
+ let oPostKey = o.postKey || "";
717
+
718
+ if (k == 0 && o.Status == 0) {
719
+ s.append(
720
+ oPreKey +
721
+ '"' +
722
+ o.key.toString() +
723
+ '"' +
724
+ oPostKey +
725
+ ":" +
726
+ o.jsonString
727
+ );
728
+ } else {
729
+ this._status = -53;
730
+ this.AddStatusMessage(
731
+ "ERROR: Failed to serialize Object item " +
732
+ i +
733
+ " [err-53][" +
734
+ o.Status +
735
+ ":" +
736
+ o.StatusMessage +
737
+ "]"
738
+ );
739
+ return -1;
740
+ }
741
+ i++;
742
+ }); // end foreach
743
+ s.append("}");
744
+ break;
745
+ case "array":
746
+ s.append("[");
747
+ i = 0;
748
+ this._value.forEach((o) => {
749
+ if (i > 0) {
750
+ s.append(",");
751
+ }
752
+ k = o.SerializeMe();
753
+ if (k == 0 && o.Status == 0) {
754
+ s.append(o.jsonString);
755
+ } else {
756
+ this._status = -52;
757
+ this.AddStatusMessage(
758
+ "ERROR: Failed to serialize Array item " + i + " [err-52]"
759
+ );
760
+ return -1;
761
+ }
762
+ i++;
763
+ });
764
+ s.append("]");
765
+ break;
766
+ case "null":
767
+ s.append("null");
768
+ break;
769
+ case "string":
770
+ s.append('"' + this.EncodeString(this.toStr()) + '"');
771
+ break;
772
+ default:
773
+ s.append(this.toStr()); // FUTURE: better approach? seems prone to problems
774
+ break;
775
+ }
776
+ let postSpace = "" + (this.postSpace || "") + (this.finalSpace || "");
777
+ if (postSpace) {
778
+ s.append(postSpace);
779
+ }
780
+ this._jsonString = s.toString();
781
+ this._jsonString_valid = true;
782
+ } catch (err94) {
783
+ this._jsonString = "";
784
+ this.InvalidateJsonString(1);
785
+ this._status = -94;
786
+ // FUTURE: Remove err94.message below?
787
+ this.AddStatusMessage(
788
+ "ERROR: SerializeMe() failed to serialize the FlexJson object. [err-94] " +
789
+ err94.message
790
+ );
791
+ return -1;
792
+ }
793
+ return 0;
794
+ } // end SerializeMe()
795
+
796
+ EncodeString(vString = "") {
797
+ let s;
798
+ // *** NOTE: This is a simple encode. It needs to be expanded in the future!
799
+ s = vString;
800
+ s = s.replace(/\\/g, "\\\\"); // **** NOTE: THIS MUST BE FIRST (so we do not double-escape the items below!)
801
+ s = s.replace(/\t/g, "\\t");
802
+ s = s.replace(/\n/g, "\\n");
803
+ s = s.replace(/\r/g, "\\r");
804
+ s = s.replace(/"/g, '\\"');
805
+ if (this.ENCODE_SINGLE_QUOTES) {
806
+ s = s.replace(/'/g, "\\'");
807
+ }
808
+ return s;
809
+ } // End Function
810
+
811
+ Deserialize(snewString, start = 0, OkToClip = false) {
812
+ if (this.trackingStats) {
813
+ IncStats("stat_Deserialize");
814
+ }
815
+ this.Clear(false, 1);
816
+ this._jsonString = snewString;
817
+ this._jsonString_valid = true;
818
+
819
+ let startPos = new FlexJsonPosition(0, start, start);
820
+ // FUTURE: determine start position LINE COUNT???
821
+ // if (start>0) { lineCount = countLines(ref _jsonString, start); }
822
+
823
+ this.DeserializeMeI(this._jsonString, startPos, OkToClip);
824
+ return this._status;
825
+ } // End Function
826
+
827
+ DeserializeFlex(snewString, start = 0, OkToClip = false) {
828
+ this.UseFlexJson = true;
829
+ this.Deserialize(snewString, start, OkToClip);
830
+ return this._status;
831
+ } // End Function
832
+
833
+ DeserializeMeI(
834
+ meJsonString,
835
+ start,
836
+ OkToClip = false,
837
+ MustBeString = false,
838
+ SearchFor1 = "*",
839
+ SearchFor2 = "*",
840
+ SearchFor3 = "*"
841
+ ) {
842
+ var c;
843
+ var c2;
844
+ let getSpace = "";
845
+ let meString = "";
846
+ //if (trackingStats) { IncStats("stat_DeserializeMeI"); } // FUTURE-NEW
847
+ this._status = 0;
848
+ this.StartPosition = start.clone(); // store in meta (if ok to do so)
849
+ let meStatus = FlexJsonConstants.ST_BEFORE_ITEM;
850
+ let quoteChar = "";
851
+ this._key = null; // *** Default
852
+ this._value = null; // *** Default
853
+ this._value_valid = false; // *** In case of error, default is invalid
854
+ this._jsonType = FlexJsonConstants.typeNull; // default
855
+ let jsonEndPoint = meJsonString.length - 1;
856
+
857
+ let mePos = start.clone(); // FlexJsonPosition USE THIS TO TRACK POSITION, THEN SET endpos ONCE DONE OR ON ERROR
858
+ let keepSP = this.keepSpacing;
859
+ let keepCM = this.keepComments;
860
+
861
+ //FlexJsonPosition startOfMe = null;
862
+ let safety = jsonEndPoint + 999;
863
+ let ok = false;
864
+ let breakBreak = false;
865
+ let storeStatus = FlexJsonConstants.ST_BEFORE_ITEM; // should not matter, but just in case
866
+ while (
867
+ meStatus >= 0 &&
868
+ this._status >= 0 &&
869
+ mePos.absolutePosition <= jsonEndPoint
870
+ ) {
871
+ c = meJsonString.charAt(mePos.absolutePosition);
872
+ if (mePos.absolutePosition < jsonEndPoint) {
873
+ c2 = meJsonString.substr(mePos.absolutePosition, 2);
874
+ } else {
875
+ c2 = "";
876
+ }
877
+
878
+ switch (meStatus) {
879
+ case FlexJsonConstants.ST_BEFORE_ITEM:
880
+ if (SearchFor1 != "*" && c == SearchFor1) {
881
+ breakBreak = true;
882
+ break;
883
+ } // Found search character
884
+ if (SearchFor2 != "*" && c == SearchFor2) {
885
+ breakBreak = true;
886
+ break;
887
+ } // Found search character
888
+ if (SearchFor3 != "*" && c == SearchFor3) {
889
+ breakBreak = true;
890
+ break;
891
+ } // Found search character
892
+ if (c == " " || c == "\t" || c == "\n" || c == "\r") {
893
+ // white space before item
894
+ ok = true;
895
+ if (keepSP) {
896
+ getSpace += c;
897
+ }
898
+ } else if (c == "{") {
899
+ if (MustBeString) {
900
+ this._status = -124;
901
+ this.AddStatusMessage(
902
+ "Invalid character '{' found. Expected string. @Line:" +
903
+ mePos.lineNumber +
904
+ ", @Position:" +
905
+ mePos.linePosition +
906
+ " [err-124]"
907
+ );
908
+ breakBreak = true;
909
+ break;
910
+ }
911
+ ok = true;
912
+ //startOfMe=mePos;
913
+ this._jsonType = FlexJsonConstants.typeObject;
914
+ mePos = this.DeserializeObject(meJsonString, mePos, keepSP, keepCM);
915
+ meStatus = FlexJsonConstants.ST_AFTER_ITEM;
916
+ if (this._status != 0) {
917
+ breakBreak = true;
918
+ break;
919
+ } // ERROR message should have already been generated.
920
+ } else if (c == "[") {
921
+ if (MustBeString) {
922
+ this._status = -125;
923
+ this.AddStatusMessage(
924
+ "Invalid character '[' found. Expected string. @Line:" +
925
+ mePos.lineNumber +
926
+ ", @Position:" +
927
+ mePos.linePosition +
928
+ " [err-125]"
929
+ );
930
+ breakBreak = true;
931
+ break;
932
+ }
933
+ ok = true;
934
+ //startOfMe=mePos;
935
+ this._jsonType = FlexJsonConstants.typeArray;
936
+ mePos = this.DeserializeArray(meJsonString, mePos, keepSP, keepCM);
937
+ meStatus = FlexJsonConstants.ST_AFTER_ITEM;
938
+ if (this._status != 0) {
939
+ breakBreak = true;
940
+ break;
941
+ } // ERROR message should have already been generated.
942
+ } else if (c == '"') {
943
+ ok = true;
944
+ //startOfMe=mePos;
945
+ this._jsonType = FlexJsonConstants.typeString;
946
+ quoteChar = '"';
947
+ this._stringQuote = '"';
948
+ meStatus = FlexJsonConstants.ST_STRING;
949
+ } else if (this.ALLOW_SINGLE_QUOTE_STRINGS && c == "'") {
950
+ ok = true;
951
+ //startOfMe=mePos;
952
+ this._jsonType = FlexJsonConstants.typeString;
953
+ quoteChar = "'";
954
+ this._stringQuote = "'";
955
+ meStatus = FlexJsonConstants.ST_STRING;
956
+ } else if (this._UseFlexJson) {
957
+ if (c2 == "//") {
958
+ // start of to-end-of-line comment
959
+ ok = true;
960
+ meStatus = FlexJsonConstants.ST_EOL_COMMENT;
961
+ mePos.increment(); // so we skip 2 characters
962
+ if (keepCM) {
963
+ getSpace += c2;
964
+ }
965
+ }
966
+ if (c2 == "/*") {
967
+ // start of asterix comment
968
+ ok = true;
969
+ meStatus = FlexJsonConstants.ST_AST_COMMENT;
970
+ mePos.increment(); // so we skip 2 characters
971
+ if (keepCM) {
972
+ getSpace += c2;
973
+ }
974
+ }
975
+ }
976
+ // With or without FlexJSON, we allow simple strings without quotes - cannot contain commas, spaces, special characters, etc
977
+ // Later we will determine if this is a number, boolean, null, or unquoted-string (string is only allowed with FlexJSON)
978
+ if (
979
+ (c >= "A" && c <= "Z") ||
980
+ (c >= "a" && c <= "z") ||
981
+ (c >= "0" && c <= "9") ||
982
+ c == "-" ||
983
+ c == "_" ||
984
+ c == "."
985
+ ) {
986
+ ok = true;
987
+ //startOfMe=mePos;
988
+ this._jsonType = "nqstring"; // NOTE! THIS IS NOT A REAL TYPE - IT IS A FLAG FOR LATER
989
+ this._stringQuote = " ";
990
+ meStatus = FlexJsonConstants.ST_STRING_NO_QUOTE;
991
+ }
992
+ if (!ok) {
993
+ // generate error condition - invalid character
994
+ this._status = -102;
995
+ this.AddStatusMessage("ERROR: Invalid charater. [err-102]");
996
+ breakBreak = true;
997
+ break;
998
+ }
999
+ // if we are no longer in pre-space territory, the we need to store the whitespace/comments
1000
+ if (meStatus >= FlexJsonConstants.ST_STRING) {
1001
+ if (keepCM || keepSP) {
1002
+ preSpace = getSpace;
1003
+ getSpace = ""; // clear
1004
+ }
1005
+ }
1006
+ if (
1007
+ meStatus == FlexJsonConstants.ST_STRING ||
1008
+ meStatus == FlexJsonConstants.ST_STRING_NO_QUOTE
1009
+ ) {
1010
+ meString = "";
1011
+ if (c != '"' && c != "'") {
1012
+ meString += c;
1013
+ }
1014
+ }
1015
+ break;
1016
+ case FlexJsonConstants.ST_AFTER_ITEM:
1017
+ if (SearchFor1 != "*" && c == SearchFor1) {
1018
+ breakBreak = true;
1019
+ break;
1020
+ } // Found search character
1021
+ if (SearchFor2 != "*" && c == SearchFor2) {
1022
+ breakBreak = true;
1023
+ break;
1024
+ } // Found search character
1025
+ if (SearchFor3 != "*" && c == SearchFor3) {
1026
+ breakBreak = true;
1027
+ break;
1028
+ } // Found search character
1029
+ if (c == " " || c == "\t" || c == "\n" || c == "\r") {
1030
+ // white space before item
1031
+ ok = true;
1032
+ if (keepSP) {
1033
+ getSpace += c;
1034
+ }
1035
+ }
1036
+ if (this._UseFlexJson) {
1037
+ if (c2 == "//") {
1038
+ // start of to-end-of-line comment
1039
+ ok = true;
1040
+ storeStatus = meStatus;
1041
+ meStatus = FlexJsonConstants.ST_EOL_COMMENT_POST;
1042
+ mePos.increment(); // so we skip 2 characters
1043
+ if (keepCM) {
1044
+ getSpace += c2;
1045
+ }
1046
+ }
1047
+ if (c2 == "/*") {
1048
+ // start of asterix comment
1049
+ ok = true;
1050
+ storeStatus = meStatus;
1051
+ meStatus = FlexJsonConstants.ST_AST_COMMENT_POST;
1052
+ mePos.increment(); // so we skip 2 characters
1053
+ if (keepCM) {
1054
+ getSpace += c2;
1055
+ }
1056
+ }
1057
+ }
1058
+ if (!ok) {
1059
+ // generate error condition (unless OKToClip)
1060
+ if (OkToClip) {
1061
+ // if we are keeping comments+ then we should probably grab this garbage text
1062
+ // This will allow us to modify a FlexJSON text file/block and write it back "AS IS"
1063
+ if (keepCM) {
1064
+ let finalGarbage = meJsonString.substr(
1065
+ mePos.absolutePosition,
1066
+ meJsonString.length - mePos.absolutePosition
1067
+ );
1068
+ getSpace += finalGarbage;
1069
+ }
1070
+ breakBreak = true;
1071
+ break;
1072
+ }
1073
+ this._status = -192;
1074
+ this.AddStatusMessage(
1075
+ "ERROR: Additional text found after end of JSON. [err-192]"
1076
+ );
1077
+ breakBreak = true;
1078
+ break;
1079
+ }
1080
+ break;
1081
+ case FlexJsonConstants.ST_EOL_COMMENT:
1082
+ case FlexJsonConstants.ST_EOL_COMMENT_POST:
1083
+ if (c2 == FlexJsonConstants.NEWLINE) {
1084
+ ok = true;
1085
+ if (keepSP) {
1086
+ getSpace += c2;
1087
+ }
1088
+ meStatus = storeStatus;
1089
+
1090
+ mePos.incrementLine(2);
1091
+ continue; // NOTE: Here we must skip the end of the do loop so that we do not increment the counter again
1092
+ } else if (c == "\n" || c == "\r") {
1093
+ ok = true;
1094
+ if (keepSP) {
1095
+ getSpace += c;
1096
+ }
1097
+ meStatus = storeStatus;
1098
+ } else {
1099
+ // absorb all comment characters
1100
+ ok = true;
1101
+ if (keepSP) {
1102
+ getSpace += c;
1103
+ }
1104
+ }
1105
+ break;
1106
+ case FlexJsonConstants.ST_AST_COMMENT:
1107
+ case FlexJsonConstants.ST_AST_COMMENT_POST:
1108
+ if (c2 == "*/") {
1109
+ ok = true;
1110
+ if (keepSP) {
1111
+ getSpace += c2;
1112
+ }
1113
+ meStatus = storeStatus;
1114
+
1115
+ mePos.increment(); // increment by 1 here - increments again at bottom of do loop
1116
+ } else {
1117
+ // absorb all comment characters
1118
+ ok = true;
1119
+ if (keepSP) {
1120
+ getSpace += c;
1121
+ }
1122
+ }
1123
+ break;
1124
+ case FlexJsonConstants.ST_STRING:
1125
+ if (c == quoteChar) {
1126
+ // we reached the end of the string
1127
+ ok = true;
1128
+ meStatus = FlexJsonConstants.ST_AFTER_ITEM;
1129
+ } else if (c == "\\") {
1130
+ // escaped character
1131
+ mePos.increment();
1132
+ c = meJsonString[mePos.absolutePosition];
1133
+ switch (c) {
1134
+ case "\\":
1135
+ case "/":
1136
+ case "'":
1137
+ case '"':
1138
+ meString += c;
1139
+ break;
1140
+ case "t":
1141
+ meString += "\t"; //Tab character
1142
+ break;
1143
+ case "b":
1144
+ meString += Convert.ToChar(8);
1145
+ break;
1146
+ case "c":
1147
+ meString += Convert.ToChar(13);
1148
+ break;
1149
+ case "n":
1150
+ meString += "\n"; //New Line Character
1151
+ break;
1152
+ case "r":
1153
+ meString += "\r"; //LineFeedCarriageReturn
1154
+ break;
1155
+ case "v":
1156
+ meString += "*"; // ***** FUTURE: Need to determine this character!
1157
+ break;
1158
+ case "u":
1159
+ // *** Here we need to get the next 4 digits and turn them into a character
1160
+ if (mePos.absolutePosition > jsonEndPoint - 4) {
1161
+ this._status = -157;
1162
+ this.AddStatusMessage(
1163
+ "ERROR: Invalid \\u escape sequence. [err-157]"
1164
+ );
1165
+ breakBreak = true;
1166
+ break;
1167
+ }
1168
+ c2 = substr(meJsonString, mePos.absolutePosition + 1, 4);
1169
+ if (IsHex4(c2)) {
1170
+ // FUTURE-NEW!!! FIX THIS! TODO - NOW- PROBLEM!!!
1171
+ let asciiValue = "####"; //System.Convert.ToChar(System.Convert.ToUInt32(c, 16)) + "";
1172
+ meString += asciiValue;
1173
+ mePos.increment(4);
1174
+ } else {
1175
+ // *** Invalid format
1176
+ this._status = -151;
1177
+ this.AddStatusMessage(
1178
+ "Expected \\u escape sequence to be followed by a valid four-digit hex value @Line:" +
1179
+ mePos.lineNumber +
1180
+ " @Position:" +
1181
+ mePos.linePosition +
1182
+ " (e-151)"
1183
+ );
1184
+ breakBreak = true;
1185
+ break;
1186
+ }
1187
+ break;
1188
+ default:
1189
+ // *** Invalid format
1190
+ this._status = -152;
1191
+ this.AddStatusMessage(
1192
+ "The \\ escape character is not followed by a valid value @Line:" +
1193
+ mePos.lineNumber +
1194
+ " @Position:" +
1195
+ mePos.linePosition +
1196
+ " (e-152)"
1197
+ );
1198
+ breakBreak = true;
1199
+ break;
1200
+ } //End switch
1201
+ } else {
1202
+ meString += c;
1203
+ }
1204
+ break;
1205
+ case FlexJsonConstants.ST_STRING_NO_QUOTE:
1206
+ if (SearchFor1 != "*" && c == SearchFor1) {
1207
+ breakBreak = true;
1208
+ break;
1209
+ } // Found search character
1210
+ if (SearchFor2 != "*" && c == SearchFor2) {
1211
+ breakBreak = true;
1212
+ break;
1213
+ } // Found search character
1214
+ if (SearchFor3 != "*" && c == SearchFor3) {
1215
+ breakBreak = true;
1216
+ break;
1217
+ } // Found search character
1218
+ if (
1219
+ (c >= "A" && c <= "Z") ||
1220
+ (c >= "a" && c <= "z") ||
1221
+ (c >= "0" && c <= "9") ||
1222
+ c == "-" ||
1223
+ c == "_" ||
1224
+ c == "."
1225
+ ) {
1226
+ ok = true;
1227
+ meString += c;
1228
+ } else if (c2 == FlexJsonConstants.NEWLINE) {
1229
+ // consume this as whitespace
1230
+ ok = true;
1231
+ if (keepSP) {
1232
+ getSpace += c2;
1233
+ }
1234
+ mePos.incrementLine(2);
1235
+ meStatus = FlexJsonConstants.ST_AFTER_ITEM;
1236
+ continue; // so that we do not increment mePos again
1237
+ } else if (c == " " || c == "\t" || c == "\r" || c == "\n") {
1238
+ // consume this as whitespace
1239
+ ok = true;
1240
+ if (keepSP) {
1241
+ getSpace += c;
1242
+ }
1243
+ meStatus = FlexJsonConstants.ST_AFTER_ITEM;
1244
+ } else {
1245
+ // not a valid character
1246
+ this._status = -159;
1247
+ this.AddStatusMessage(
1248
+ "Invalid character found in non-quoted string: char:[" +
1249
+ c +
1250
+ "] @Line:" +
1251
+ mePos.lineNumber +
1252
+ " @Position:" +
1253
+ mePos.linePosition +
1254
+ " (e-159)"
1255
+ );
1256
+ breakBreak = true;
1257
+ break;
1258
+ }
1259
+ break;
1260
+ }
1261
+ if (breakBreak) {
1262
+ break;
1263
+ }
1264
+ // move to next character
1265
+ if (c == "\n") {
1266
+ mePos.incrementLine(1);
1267
+ } else {
1268
+ mePos.increment();
1269
+ }
1270
+ safety--;
1271
+ if (safety <= 0) {
1272
+ this._status = -169;
1273
+ this.AddStatusMessage(
1274
+ "Maximum iterations reached: DeserializeMe(): @Line:" +
1275
+ mePos.lineNumber +
1276
+ " @Position:" +
1277
+ mePos.linePosition +
1278
+ " (e-169)"
1279
+ );
1280
+ break;
1281
+ }
1282
+ }
1283
+
1284
+ if (this._status == 0) {
1285
+ switch (this._jsonType) {
1286
+ case FlexJsonConstants.typeString:
1287
+ this._value = meString;
1288
+ break;
1289
+ case "nqstring": // NOTE! This is objType must be converted to another type here! nqstring is not a real type.
1290
+ let tmpString = meString; // do not need to trim since we should have consumed the whitespace in getSpace
1291
+ if (!MustBeString) {
1292
+ if (tmpString.length < 7) {
1293
+ var tmpStringUpper = tmpString.trim().toUpperCase();
1294
+ if (tmpStringUpper == "" || tmpStringUpper == "NULL") {
1295
+ this._value = "";
1296
+ this._jsonType = FlexJsonConstants.typeNull;
1297
+ } else if (tmpStringUpper == "TRUE") {
1298
+ this._value = true;
1299
+ this._jsonType = FlexJsonConstants.typeBoolean;
1300
+ } else if (tmpStringUpper == "FALSE") {
1301
+ this._value = false;
1302
+ this._jsonType = FlexJsonConstants.typeBoolean;
1303
+ }
1304
+ }
1305
+ // If the above did not match, lets see if this text is numeric...
1306
+ if (this._jsonType == "nqstring") {
1307
+ try {
1308
+ valueCheck = parseFloat(tmpString);
1309
+ if (!isNaN(valueCheck)) {
1310
+ this._value = valueCheck;
1311
+
1312
+ // It worked... keep going...
1313
+ this._jsonType = FlexJsonConstants.typeNumber;
1314
+ }
1315
+ } catch (Exception) {}
1316
+ }
1317
+ }
1318
+ // If STILL not identified, then it must be a STRING (ONLY valid as an unquoted string if using FlexJson!)
1319
+ if (this._jsonType == "nqstring") {
1320
+ this._value = tmpString;
1321
+ this._jsonType = FlexJsonConstants.typeString;
1322
+ if (!this._UseFlexJson) {
1323
+ // ERROR: we found an unquoted string and we are not using FlexJson
1324
+ // NOTE! Error occurred at the START of this item
1325
+ this._status = -199;
1326
+ this.AddStatusMessage(
1327
+ "Encountered unquoted string: @Line:" +
1328
+ start.lineNumber +
1329
+ " @Position:" +
1330
+ start.linePosition +
1331
+ " (e-199)"
1332
+ );
1333
+ }
1334
+ }
1335
+ break;
1336
+ // NOTE: other cases fall through: object, array, null(default if nothing found)
1337
+ } // end switch
1338
+ } // end if (this._status != 0)
1339
+
1340
+ // indicate if the new _value is valid
1341
+ if (this._status == 0) {
1342
+ this._value_valid = true;
1343
+ } else {
1344
+ this._value_valid = false;
1345
+ }
1346
+
1347
+ // Store original JSON string and mark as "valid"
1348
+ if (this._status == 0) {
1349
+ this._jsonString = meJsonString.substr(
1350
+ start.absolutePosition,
1351
+ mePos.absolutePosition - start.absolutePosition
1352
+ );
1353
+ this._jsonString_valid = true;
1354
+ } else {
1355
+ this._jsonString = meJsonString;
1356
+ this._jsonString_valid = false;
1357
+ }
1358
+ if (getSpace != null) {
1359
+ this.finalSpace = getSpace;
1360
+ }
1361
+ this.EndPosition = mePos;
1362
+
1363
+ return mePos;
1364
+ }
1365
+
1366
+ CreatePartClone(keepSP = false, keepCM = false) {
1367
+ let jClone = new FlexJson();
1368
+ jClone.UseFlexJson = this.UseFlexJson;
1369
+ jClone.ALLOW_SINGLE_QUOTE_STRINGS = this.ALLOW_SINGLE_QUOTE_STRINGS;
1370
+ return jClone;
1371
+ }
1372
+
1373
+ DeserializeObject(meJsonString, start, keepSP, keepCM) {
1374
+ var j2;
1375
+ var jNew;
1376
+ let Key = "";
1377
+ var c;
1378
+
1379
+ let v = [];
1380
+ let mePos = start;
1381
+ mePos.increment(); // *** IMPORTANT! Move past { symbol
1382
+ let skipToNext = false;
1383
+
1384
+ let safety = 99999;
1385
+ while (true) {
1386
+ skipToNext = false;
1387
+ j2 = this.CreatePartClone(keepSP, keepCM);
1388
+ let newPos = j2.DeserializeMeI(
1389
+ meJsonString,
1390
+ mePos.clone(),
1391
+ true,
1392
+ true,
1393
+ ":",
1394
+ ",",
1395
+ "}"
1396
+ ); // finding a } means there are no more items in the object
1397
+
1398
+ if (j2._status == 0 && j2._jsonType == FlexJsonConstants.typeNull) {
1399
+ // special case where item is NULL/BLANK - ok to skip...
1400
+ c = meJsonString.charAt(newPos.absolutePosition);
1401
+ if (c == "," || c == "}") {
1402
+ mePos = newPos;
1403
+ skipToNext = true;
1404
+ } else {
1405
+ this.StatusErr(
1406
+ -18,
1407
+ "Failed to find key in key:value pair @Line:" +
1408
+ mePos.lineNumber +
1409
+ " @Position:" +
1410
+ mePos.linePosition +
1411
+ " (e-18) [" +
1412
+ j2.StatusMessage +
1413
+ "]"
1414
+ );
1415
+ break;
1416
+ }
1417
+ } else if (j2._status != 0) {
1418
+ this.StatusErr(
1419
+ -19,
1420
+ "Failed to find key in key:value pair @Line:" +
1421
+ mePos.lineNumber +
1422
+ " @Position:" +
1423
+ mePos.linePosition +
1424
+ " (e-19) [" +
1425
+ j2.StatusMessage +
1426
+ "]"
1427
+ );
1428
+ break;
1429
+ // }
1430
+ } else if (j2._jsonType != "string") {
1431
+ this.StatusErr(
1432
+ -21,
1433
+ "Key name in key:value pair must be a string @Line:" +
1434
+ mePos.lineNumber +
1435
+ " @Position:" +
1436
+ mePos.linePosition +
1437
+ " (e-21)"
1438
+ );
1439
+ break;
1440
+ } else {
1441
+ // capture the white space/comments here
1442
+ if (keepCM || keepSP) {
1443
+ this.preKey = j2.preSpace;
1444
+ this.postKey = j2.finalSpace;
1445
+ }
1446
+ Key = j2._value; // already verified that this is type "string"
1447
+ this._keyQuote = j2._stringQuote;
1448
+ mePos = newPos;
1449
+ }
1450
+
1451
+ j2 = null;
1452
+
1453
+ if (!skipToNext) {
1454
+ let cColon = "\0";
1455
+ if (mePos.absolutePosition < meJsonString.length) {
1456
+ cColon = meJsonString.charAt(mePos.absolutePosition);
1457
+ }
1458
+ // *** Consume the :
1459
+ if (cColon != ":") {
1460
+ this.StatusErr(
1461
+ -16,
1462
+ "Expected : symbol in key:value pair @Line:" +
1463
+ mePos.lineNumber +
1464
+ " @Position:" +
1465
+ mePos.linePosition +
1466
+ " (e-16)"
1467
+ );
1468
+ break;
1469
+ }
1470
+ mePos.increment();
1471
+
1472
+ // *** Expecting value after the : symbol in value/pair combo
1473
+ // Space/comments here are after the separator
1474
+ jNew = this.CreatePartClone(keepSP, keepCM);
1475
+ let finalPos = jNew.DeserializeMeI(
1476
+ meJsonString,
1477
+ mePos.clone(),
1478
+ true,
1479
+ false,
1480
+ ",",
1481
+ "}"
1482
+ );
1483
+
1484
+ // Check for blank=null (return status -11 and _jsonType=null)
1485
+ // DEBUGGER: FUTURE-NOW: Should not be looking for -11???
1486
+ if (
1487
+ jNew.Status == -11 &&
1488
+ jNew.jsonType == "null" &&
1489
+ _UseFlexJson == true
1490
+ ) {
1491
+ // Note: jNew.status=-11 indicates nothing was found where there should have been a value - for FLEX JSON this is legitimate.
1492
+ jNew._status = 0; // this is OK
1493
+ }
1494
+ if (jNew.Status != 0) {
1495
+ this.StatusErr(
1496
+ -21,
1497
+ "Failed to find value in key:value pair. @Line:" +
1498
+ mePos.lineNumber +
1499
+ " @Position:" +
1500
+ mePos.linePosition +
1501
+ " (e-21) [" +
1502
+ jNew.StatusMessage +
1503
+ "]"
1504
+ );
1505
+ break;
1506
+ } else {
1507
+ // Note: Above, jNew.status=-11 indicates nothing was found where there should have been a value - for FLEX JSON this is legitimate.
1508
+ // *** For all cases: object, array, string, number, boolean, or null
1509
+ jNew.Parent = this;
1510
+ jNew._key = Key;
1511
+ v.push(jNew); // FUTURE: IS THIS WRONG? SHOULD WE CHECK TO SEE IF THE KEY ALREADY EXISTS? AS IS, THE FIRST VALUE WILL "overshadow" ANY SUBSEQUENT VALUE. MAYBE THIS IS OK.
1512
+ mePos = finalPos;
1513
+ }
1514
+
1515
+ jNew = null;
1516
+
1517
+ // *** Check if we are past the end of the string
1518
+ if (mePos.absolutePosition >= meJsonString.length) {
1519
+ this.StatusErr(
1520
+ -15,
1521
+ "Past end of string before we found the } symbol as the end of the object @Line:" +
1522
+ mePos.lineNumber +
1523
+ " @Position:" +
1524
+ mePos.linePosition +
1525
+ " (e-15)"
1526
+ );
1527
+ break;
1528
+ }
1529
+ } // end if (!skipToNext)
1530
+
1531
+ let cNext = meJsonString[mePos.absolutePosition];
1532
+ // *** Check if we reached the end of the object
1533
+ if (cNext == "}") {
1534
+ break;
1535
+ } // return. we are done buildng the JSON object
1536
+ // *** Comma required between items in object
1537
+ if (cNext != ",") {
1538
+ this.StatusErr(
1539
+ -17,
1540
+ "Expected , symbol to separate value pairs @Line:" +
1541
+ mePos.lineNumber +
1542
+ " @Position:" +
1543
+ mePos.linePosition +
1544
+ " (e-17)"
1545
+ );
1546
+ break;
1547
+ }
1548
+ mePos.increment();
1549
+
1550
+ safety--;
1551
+ if (safety <= 0) {
1552
+ this.StatusErr(
1553
+ -117,
1554
+ "Max deserialization iterations reached in DeserializeObject. @Line:" +
1555
+ mePos.lineNumber +
1556
+ " @Position:" +
1557
+ mePos.linePosition +
1558
+ " (e-117)"
1559
+ );
1560
+ break;
1561
+ }
1562
+ } // end do
1563
+
1564
+ this._value = v;
1565
+ jNew = null;
1566
+ j2 = null;
1567
+ return mePos; // negative value indicated an error
1568
+ } // End DeserializeObject2()
1569
+
1570
+ // DeserializeArray()
1571
+ // *** Look for comma separated value list as a JSON array
1572
+ // NOTE: Parent routine DeserializeMeI() keeps track of the before/after spacing of the array
1573
+ DeserializeArray(meJsonString, start, keepSP, keepCM) {
1574
+ var jNew;
1575
+
1576
+ let v = [];
1577
+ let mePos = start;
1578
+ mePos.increment(); // *** IMPORTANT! Move past [ symbol
1579
+
1580
+ let safety = 99999;
1581
+ while (true) {
1582
+ // *** Expecting value
1583
+ jNew = this.CreatePartClone(keepSP, keepCM);
1584
+ let finalPos = jNew.DeserializeMeI(
1585
+ meJsonString,
1586
+ mePos.clone(),
1587
+ true,
1588
+ false,
1589
+ ",",
1590
+ "]"
1591
+ );
1592
+
1593
+ if (jNew.Status != 0) {
1594
+ this.StatusErr(
1595
+ -21,
1596
+ "Failed to find value in array. @Line:" +
1597
+ mePos.lineNumber +
1598
+ " @Position:" +
1599
+ mePos.linePosition +
1600
+ " (e-21) [" +
1601
+ jNew.StatusMessage +
1602
+ "]"
1603
+ );
1604
+ break;
1605
+ } else {
1606
+ // Note: Above, jNew.status=-11 indicates nothing was found where there should have been a value - for FLEX JSON this is legitimate.
1607
+ // *** For all cases: object, array, string, number, boolean, or null
1608
+ jNew.Parent = this;
1609
+ // If jNew is a blank string, then we need to create an empty array rather than an array with a null value.
1610
+ if (jNew._jsonString.trim() != "") {
1611
+ v.push(jNew);
1612
+ }
1613
+ mePos = finalPos;
1614
+ }
1615
+
1616
+ jNew = null;
1617
+
1618
+ // *** Check if we are past the end of the string
1619
+ if (mePos.absolutePosition >= meJsonString.length) {
1620
+ this.StatusErr(
1621
+ -115,
1622
+ "Past end of string before we found the ] symbol as the end of the object @Line:" +
1623
+ mePos.lineNumber +
1624
+ " @Position:" +
1625
+ mePos.linePosition +
1626
+ " (e-115)"
1627
+ );
1628
+ break;
1629
+ }
1630
+
1631
+ let cNext = meJsonString[mePos.absolutePosition];
1632
+ // *** Check if we reached the end of the object
1633
+ if (cNext == "]") {
1634
+ break;
1635
+ } // return. we are done buildng the JSON object
1636
+ // *** Comma required between items in object
1637
+ if (cNext != ",") {
1638
+ this.StatusErr(
1639
+ -37,
1640
+ "Expected , symbol to separate values @Line:" +
1641
+ mePos.lineNumber +
1642
+ " @Position:" +
1643
+ mePos.linePosition +
1644
+ " (e-37)"
1645
+ );
1646
+ break;
1647
+ }
1648
+ mePos.increment();
1649
+
1650
+ safety--;
1651
+ if (safety <= 0) {
1652
+ this.StatusErr(
1653
+ -119,
1654
+ "Max deserialization iterations reached in DeserializeObject. @Line:" +
1655
+ mePos.lineNumber +
1656
+ " @Position:" +
1657
+ mePos.linePosition +
1658
+ " (e-119)"
1659
+ );
1660
+ break;
1661
+ }
1662
+ } // end do
1663
+
1664
+ this._value = v;
1665
+ jNew = null;
1666
+ return mePos; // negative value indicated an error
1667
+ } // End DeserializeArray()
1668
+ DeserializeFile(FilePath, OkToClip = false) {
1669
+ //if ( !System.IO.File.Exists(FilePath) ) { _status=-31; return _status; }
1670
+ try {
1671
+ /*
1672
+ fs.readFile(FilePath, (err, data) => {
1673
+ if (err) throw err;
1674
+
1675
+ const fsData = data.toString();
1676
+ this.Deserialize(fsData,0,OkToClip);
1677
+ })
1678
+ */
1679
+ const fsData = fs.readFileSync(FilePath, "utf8");
1680
+ this.Deserialize(fsData, 0, OkToClip);
1681
+ } catch (err) {
1682
+ this._status = -32;
1683
+ this.statusMsg = err.message;
1684
+ return this._status;
1685
+ }
1686
+ }
1687
+
1688
+ DeserializeFlexFile(
1689
+ FilePath,
1690
+ OkToClip = false,
1691
+ spacing_flag = -1,
1692
+ comments_flag = -1
1693
+ ) {
1694
+ this.UseFlexJson = true;
1695
+ if (spacing_flag >= 0 || comments_flag >= 0) {
1696
+ this.keepSpacingAndComments(spacing_flag, comments_flag);
1697
+ }
1698
+ return this.DeserializeFile(FilePath, OkToClip);
1699
+ }
1700
+
1701
+ StatusErr(nErr, strErr) {
1702
+ this._status = nErr;
1703
+ this.AddStatusMessage(strErr);
1704
+ } // End
1705
+
1706
+ // FUTURE: Replace this with this.statusMsg=...
1707
+ // AddStatusMessage()
1708
+ // Set _statusMsg to the new message/error string
1709
+ // Also add the message to a log in stats object, if stats are being tracked.
1710
+ AddStatusMessage(msg) {
1711
+ this.statusMsg = msg;
1712
+ if (this.NoStatsOrMsgs) {
1713
+ return;
1714
+ }
1715
+ if (!this.trackingStats) {
1716
+ return;
1717
+ } // verifies that _meta.stats exists as an object
1718
+ }
1719
+
1720
+ // Convert Javascript type to FlexJson type
1721
+ convertType(v) {
1722
+ if (v === null) {
1723
+ return "null";
1724
+ }
1725
+ const vType = typeof v;
1726
+ switch (vType) {
1727
+ case "bigint":
1728
+ case "number":
1729
+ return "number";
1730
+ break;
1731
+ case "boolean":
1732
+ return "boolean";
1733
+ break;
1734
+ case "object": // must be FlexJson object or it is not a true "object"
1735
+ return ""; // indicates an error/invlid type
1736
+ break;
1737
+ case "FlexJson":
1738
+ return FlexJson.jsonType;
1739
+ break;
1740
+ case "function":
1741
+ return ""; // indicate error/invalid type
1742
+ break;
1743
+ default:
1744
+ return "string";
1745
+ break;
1746
+ }
1747
+ }
1748
+ }
1749
+
1750
+ module.exports = FlexJson;