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