flex-json 0.0.5 → 0.0.6

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