json-as 0.6.6 → 0.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -38,6 +38,7 @@ class Vec3 {
38
38
 
39
39
  @json
40
40
  class Player {
41
+ @alias("first name")
41
42
  firstName!: string;
42
43
  lastName!: string;
43
44
  lastActive!: i32[];
@@ -202,4 +202,148 @@ describe("Deser externals", () => {
202
202
  body: '{\n "args": {},\n "headers": {\n "content-length": "0",\n "accept": "*/*" \n}}'
203
203
  })
204
204
  })
205
- });
205
+ });
206
+
207
+ describe("Ser/de Maps", () => {
208
+ it("should serialize Map<string, string>", () => {
209
+ const m = new Map<string, string>();
210
+ m.set("a", "x");
211
+ m.set("b", "y");
212
+ m.set("c", "z");
213
+ canSer(m, '{"a":"x","b":"y","c":"z"}');
214
+ });
215
+ it("should serialize Map<string, string | null>", () => {
216
+ const m = new Map<string, string | null>();
217
+ m.set("a", "x");
218
+ m.set("b", "y");
219
+ m.set("c", null);
220
+ canSer(m, '{"a":"x","b":"y","c":null}');
221
+ });
222
+ it("should serialize Map<string, string[]]>", () => {
223
+ const m = new Map<string, string[]>();
224
+ m.set("a", ["a1", "a2", "a3"]);
225
+ m.set("b", ["b1", "b2", "b3"]);
226
+ m.set("c", ["c1", "c2", "c3"]);
227
+ canSer(m, '{"a":["a1","a2","a3"],"b":["b1","b2","b3"],"c":["c1","c2","c3"]}');
228
+ });
229
+ it("should serialize Map<string, u32>", () => {
230
+ const m = new Map<string, u32>();
231
+ m.set("a", 1);
232
+ m.set("b", 2);
233
+ m.set("c", 3);
234
+ canSer(m, '{"a":1,"b":2,"c":3}');
235
+ });
236
+ it("should serialize Map<string, bool>", () => {
237
+ const m = new Map<string, bool>();
238
+ m.set("a", true);
239
+ m.set("b", false);
240
+ m.set("c", true);
241
+ canSer(m, '{"a":true,"b":false,"c":true}');
242
+ });
243
+ it("should serialize Map<string, Vec3>", () => {
244
+ const m = new Map<string, Vec3>();
245
+ m.set("foo", { x: 1, y: 2, z: 3 });
246
+ m.set("bar", { x: 11.5, y: 12.5, z: 13.5 });
247
+ canSer(m, '{"foo":{"x":1.0,"y":2.0,"z":3.0},"bar":{"x":11.5,"y":12.5,"z":13.5}}');
248
+ });
249
+
250
+ it("should deserialize Map<string, string>", () => {
251
+ const m = new Map<string, string>();
252
+ m.set("a", "x");
253
+ m.set("b", "y");
254
+ m.set("c", "z");
255
+ canDeser('{"a":"x","b":"y","c":"z"}', m);
256
+ });
257
+ it("should deserialize Map<string, string | null>", () => {
258
+ const m = new Map<string, string | null>();
259
+ m.set("a", "x");
260
+ m.set("b", "y");
261
+ m.set("c", null);
262
+ canDeser('{"a":"x","b":"y","c":null}', m);
263
+ });
264
+ it("should deserialize Map<string, string[]>", () => {
265
+ const m = new Map<string, string[]>();
266
+ m.set("a", ["a1", "a2", "a3"]);
267
+ m.set("b", ["b1", "b2", "b3"]);
268
+ m.set("c", ["c1", "c2", "c3"]);
269
+ canDeser('{"a":["a1","a2","a3"],"b":["b1","b2","b3"],"c":["c1","c2","c3"]}', m);
270
+ });
271
+ it("should deserialize Map<string, u32>", () => {
272
+ const m = new Map<string, u32>();
273
+ m.set("a", 1);
274
+ m.set("b", 2);
275
+ m.set("c", 3);
276
+ canDeser('{"a":1,"b":2,"c":3}', m);
277
+ });
278
+ it("should deserialize Map<string, bool>", () => {
279
+ const m = new Map<string, bool>();
280
+ m.set("a", true);
281
+ m.set("b", false);
282
+ m.set("c", true);
283
+ canDeser('{"a":true,"b":false,"c":true}', m);
284
+ });
285
+ it("should deserialize Map<string, Vec3>", () => {
286
+ const m = new Map<string, Vec3>();
287
+ m.set("foo", { x: 1, y: 2, z: 3 });
288
+ m.set("bar", { x: 11.5, y: 12.5, z: 13.5 });
289
+ canDeser('{"foo":{"x":1.0,"y":2.0,"z":3.0},"bar":{"x":11.5,"y":12.5,"z":13.5}}', m);
290
+ });
291
+
292
+ it("should serialize Map<u32, bool>", () => {
293
+ const m = new Map<u32, bool>();
294
+ m.set(1, true);
295
+ m.set(2, false);
296
+ m.set(3, true);
297
+ canSer(m, '{"1":true,"2":false,"3":true}');
298
+ });
299
+ it("should deserialize Map<u32, bool>", () => {
300
+ const m = new Map<u32, bool>();
301
+ m.set(1, true);
302
+ m.set(2, false);
303
+ m.set(3, true);
304
+ canDeser('{"1":true,"2":false,"3":true}', m);
305
+ });
306
+
307
+ it("should serialize Map<bool, string>", () => {
308
+ const m = new Map<bool, string>();
309
+ m.set(true, "a");
310
+ m.set(false, "b");
311
+ canSer(m, '{"true":"a","false":"b"}');
312
+ });
313
+ it("should deserialize Map<bool, string>", () => {
314
+ const m = new Map<bool, string>();
315
+ m.set(true, "a");
316
+ m.set(false, "b");
317
+ canDeser('{"true":"a","false":"b"}', m);
318
+ });
319
+
320
+ it("should serialize Map<string, string>[]", () => {
321
+ const m1 = new Map<string, string>();
322
+ m1.set("a", "u");
323
+ m1.set("b", "v");
324
+ m1.set("c", "w");
325
+
326
+ const m2 = new Map<string, string>();
327
+ m2.set("d", "x");
328
+ m2.set("e", "y");
329
+ m2.set("f", "z");
330
+
331
+ const a = [m1, m2];
332
+ canSer(a, '[{"a":"u","b":"v","c":"w"},{"d":"x","e":"y","f":"z"}]');
333
+ });
334
+ it("should deserialize Map<string, string>[]", () => {
335
+ const m1 = new Map<string, string>();
336
+ m1.set("a", "u");
337
+ m1.set("b", "v");
338
+ m1.set("c", "w");
339
+
340
+ const m2 = new Map<string, string>();
341
+ m2.set("d", "x");
342
+ m2.set("e", "y");
343
+ m2.set("f", "z");
344
+
345
+ const a = [m1, m2];
346
+ canDeser('[{"a":"u","b":"v","c":"w"},{"d":"x","e":"y","f":"z"}]', a);
347
+ });
348
+
349
+ });
@@ -43,13 +43,21 @@
43
43
  // @ts-ignore: Decorator is valid here
44
44
  @inline export const nullWord = "null";
45
45
  // @ts-ignore: Decorator is valid here
46
+ @inline export const leftBraceWord = "{";
47
+ // @ts-ignore: Decorator is valid here
46
48
  @inline export const leftBracketWord = "[";
47
49
  // @ts-ignore: Decorator is valid here
48
50
  @inline export const emptyArrayWord = "[]";
49
51
  // @ts-ignore: Decorator is valid here
52
+ @inline export const colonWord = ":";
53
+ // @ts-ignore: Decorator is valid here
50
54
  @inline export const commaWord = ",";
51
55
  // @ts-ignore: Decorator is valid here
56
+ @inline export const rightBraceWord = "}";
57
+ // @ts-ignore: Decorator is valid here
52
58
  @inline export const rightBracketWord = "]";
59
+ // @ts-ignore: Decorator is valid here
60
+ @inline export const quoteWord = "\"";
53
61
  // Escape Codes
54
62
  // @ts-ignore: Decorator is valid here
55
63
  @inline export const newLineCode = 10;
@@ -1,28 +1,39 @@
1
1
  import { StringSink } from "as-string-sink/assembly";
2
2
  import { isSpace } from "util/string";
3
- import { E_INVALIDDATE } from "util/error";
4
3
  import {
5
- backSlashCode,
6
- commaCode,
7
- commaWord,
4
+ aCode,
8
5
  eCode,
9
6
  fCode,
7
+ lCode,
8
+ nCode,
9
+ rCode,
10
+ sCode,
11
+ tCode,
12
+ uCode,
13
+
14
+ backSlashCode,
15
+ colonCode,
16
+ commaCode,
10
17
  leftBraceCode,
11
18
  leftBracketCode,
12
- leftBracketWord,
13
- nCode,
14
- nullWord,
19
+ newLineCode,
15
20
  quoteCode,
16
- rCode,
17
21
  rightBraceCode,
18
22
  rightBracketCode,
23
+
24
+ colonWord,
25
+ commaWord,
26
+ quoteWord,
27
+
28
+ leftBraceWord,
29
+ leftBracketWord,
30
+ rightBraceWord,
19
31
  rightBracketWord,
20
- tCode,
21
- trueWord,
22
- uCode,
23
32
  emptyArrayWord,
33
+
34
+ trueWord,
24
35
  falseWord,
25
- newLineCode,
36
+ nullWord,
26
37
  } from "./chars";
27
38
  import { snip_fast, unsafeCharCodeAt } from "./util";
28
39
  import { Virtual } from "as-virtual/assembly";
@@ -47,7 +58,7 @@ export namespace JSON {
47
58
  } else if (isBoolean<T>()) {
48
59
  return data ? "true" : "false";
49
60
  } else if (isNullable<T>() && data == null) {
50
- return "null";
61
+ return nullWord;
51
62
  // @ts-ignore
52
63
  } else if ((isInteger<T>() || isFloat<T>()) && isFinite(data)) {
53
64
  // @ts-ignore
@@ -64,7 +75,7 @@ export namespace JSON {
64
75
  return emptyArrayWord;
65
76
  // @ts-ignore
66
77
  } else if (isString<valueof<T>>()) {
67
- let result = "[";
78
+ let result = leftBracketWord;
68
79
  // @ts-ignore
69
80
  for (let i = 0; i < data.length - 1; i++) {
70
81
  // @ts-ignore
@@ -96,6 +107,20 @@ export namespace JSON {
96
107
  result.write(rightBracketWord);
97
108
  return result.toString();
98
109
  }
110
+ } else if (data instanceof Map) {
111
+ let result = new StringSink(leftBraceWord);
112
+ let keys = data.keys();
113
+ let values = data.values();
114
+ for (let i = 0; i < data.size; i++) {
115
+ result.write(serializeString(keys[i].toString()));
116
+ result.write(colonWord);
117
+ result.write(JSON.stringify(values[i]));
118
+ if (i < data.size - 1) {
119
+ result.write(commaWord);
120
+ }
121
+ }
122
+ result.write(rightBraceWord);
123
+ return result.toString();
99
124
  } else {
100
125
  throw new Error(
101
126
  `Could not serialize data of type ${nameof<T>()}. Make sure to add the correct decorators to classes.`
@@ -117,10 +142,10 @@ export namespace JSON {
117
142
  out = serializeString(data as string);
118
143
  return;
119
144
  } else if (isBoolean<T>()) {
120
- out = data ? "true" : "false";
145
+ out = data ? trueWord : falseWord;
121
146
  return;
122
147
  } else if (isNullable<T>() && data == null) {
123
- out = "null";
148
+ out = nullWord;
124
149
  return;
125
150
  // @ts-ignore
126
151
  } else if ((isInteger<T>() || isFloat<T>()) && isFinite(data)) {
@@ -133,7 +158,7 @@ export namespace JSON {
133
158
  out = data.__JSON_Serialize();
134
159
  return;
135
160
  } else if (data instanceof Date) {
136
- out = "\"" + data.toISOString() + "\"";
161
+ out = quoteWord + data.toISOString() + quoteWord;
137
162
  return;
138
163
  } else if (isArrayLike<T>()) {
139
164
  // @ts-ignore
@@ -142,7 +167,7 @@ export namespace JSON {
142
167
  return;
143
168
  // @ts-ignore
144
169
  } else if (isString<valueof<T>>()) {
145
- out = "[";
170
+ out = leftBracketWord;
146
171
  // @ts-ignore
147
172
  for (let i = 0; i < data.length - 1; i++) {
148
173
  // @ts-ignore
@@ -206,18 +231,18 @@ export namespace JSON {
206
231
  } else if (isArrayLike<T>()) {
207
232
  // @ts-ignore
208
233
  return parseArray<T>(data.trimStart());
209
- // @ts-ignore
210
- } else if (isNullable<T>() && data == "null") {
234
+ } else if (isNullable<T>() && data == nullWord) {
211
235
  // @ts-ignore
212
236
  return null;
213
237
  // @ts-ignore
214
238
  } else if (isDefined(type.__JSON_Set_Key)) {
215
239
  return parseObject<T>(data.trimStart(), initializeDefaultValues);
240
+ } else if (isMap<T>()) {
241
+ return parseMap<T>(data.trimStart(), initializeDefaultValues);
216
242
  } else if (idof<nonnull<T>>() == idof<Date>()) {
217
243
  // @ts-ignore
218
244
  return parseDate(data);
219
245
  } else {
220
- // @ts-ignore
221
246
  throw new Error(
222
247
  `Could not deserialize data ${data} to type ${nameof<T>()}. Make sure to add the correct decorators to classes.`
223
248
  );
@@ -229,7 +254,6 @@ export namespace JSON {
229
254
  @global @inline function __parseObjectValue<T>(data: string, initializeDefaultValues: boolean): T {
230
255
  let type: T;
231
256
  if (isString<T>()) {
232
- // @ts-ignore
233
257
  let result = "";
234
258
  let last = 0;
235
259
  for (let i = 0; i < data.length; i++) {
@@ -283,18 +307,18 @@ export namespace JSON {
283
307
  } else if (isArrayLike<T>()) {
284
308
  // @ts-ignore
285
309
  return parseArray<T>(data);
286
- // @ts-ignore
287
- } else if (isNullable<T>() && data == "null") {
310
+ } else if (isNullable<T>() && data == nullWord) {
288
311
  // @ts-ignore
289
312
  return null;
290
313
  // @ts-ignore
291
314
  } else if (isDefined(type.__JSON_Set_Key)) {
292
315
  return parseObject<T>(data.trimStart(), initializeDefaultValues);
316
+ } else if (isMap<T>()) {
317
+ return parseMap<T>(data.trimStart(), initializeDefaultValues);
293
318
  } else if (idof<nonnull<T>>() == idof<Date>()) {
294
319
  // @ts-ignore
295
320
  return parseDate(data);
296
321
  } else {
297
- // @ts-ignore
298
322
  throw new Error(
299
323
  `Could not deserialize data ${data} to type ${nameof<T>()}. Make sure to add the correct decorators to classes.`
300
324
  );
@@ -306,7 +330,6 @@ export namespace JSON {
306
330
  let result = new StringSink('"');
307
331
 
308
332
  let last: i32 = 0;
309
- // @ts-ignore
310
333
  for (let i = 0; i < data.length; i++) {
311
334
  const char = unsafeCharCodeAt(<string>data, i);
312
335
  if (char === 34 || char === 92) {
@@ -344,9 +367,11 @@ export namespace JSON {
344
367
  }
345
368
  }
346
369
  }
347
- if (result.length === 1) return '"' + data + '"';
348
- else result.write(<string>data, last);
349
- result.write("\"");
370
+ if (result.length === 1) {
371
+ return quoteWord + data + quoteWord;
372
+ }
373
+ result.write(<string>data, last);
374
+ result.write(quoteWord);
350
375
  return result.toString();
351
376
  }
352
377
 
@@ -410,14 +435,16 @@ export namespace JSON {
410
435
  }
411
436
  }
412
437
  }
413
- if ((data.length - 1) > last) result.write(data, last, data.length - 1);
438
+ if ((data.length - 1) > last) {
439
+ result.write(data, last, data.length - 1);
440
+ }
414
441
  return result.toString();
415
442
  }
416
443
 
417
444
  // @ts-ignore: Decorator
418
445
  @inline function parseBoolean<T extends boolean>(data: string): T {
419
- if (data.length > 3 && data.startsWith("true")) return <T>true;
420
- else if (data.length > 4 && data.startsWith("false")) return <T>false;
446
+ if (data.length > 3 && data.startsWith(trueWord)) return <T>true;
447
+ else if (data.length > 4 && data.startsWith(falseWord)) return <T>false;
421
448
  else throw new Error(`JSON: Cannot parse "${data}" as boolean`);
422
449
  }
423
450
 
@@ -437,13 +464,14 @@ export namespace JSON {
437
464
 
438
465
  // @ts-ignore: Decorator
439
466
  @inline function parseObject<T>(data: string, initializeDefaultValues: boolean): T {
440
- let schema: nonnull<T> = changetype<nonnull<T>>(
467
+ const schema: nonnull<T> = changetype<nonnull<T>>(
441
468
  __new(offsetof<nonnull<T>>(), idof<nonnull<T>>())
442
469
  );
470
+
443
471
  // @ts-ignore
444
472
  if (initializeDefaultValues) schema.__JSON_Initialize();
445
473
 
446
- let key = Virtual.createEmpty<string>();
474
+ const key = Virtual.createEmpty<string>();
447
475
  let isKey = false;
448
476
  let depth = 0;
449
477
  let outerLoopIndex = 1;
@@ -519,7 +547,12 @@ export namespace JSON {
519
547
  escaping = false;
520
548
  }
521
549
  }
522
- } else if (char == nCode) {
550
+ } else if (
551
+ char == nCode &&
552
+ unsafeCharCodeAt(data, ++outerLoopIndex) === uCode &&
553
+ unsafeCharCodeAt(data, ++outerLoopIndex) === lCode &&
554
+ unsafeCharCodeAt(data, ++outerLoopIndex) === lCode
555
+ ) {
523
556
  // @ts-ignore
524
557
  schema.__JSON_Set_Key<Virtual<string>>(key, nullWord, 0, 4, initializeDefaultValues);
525
558
  isKey = false;
@@ -534,9 +567,9 @@ export namespace JSON {
534
567
  isKey = false;
535
568
  } else if (
536
569
  char === fCode &&
537
- unsafeCharCodeAt(data, ++outerLoopIndex) === "a".charCodeAt(0) &&
538
- unsafeCharCodeAt(data, ++outerLoopIndex) === "l".charCodeAt(0) &&
539
- unsafeCharCodeAt(data, ++outerLoopIndex) === "s".charCodeAt(0) &&
570
+ unsafeCharCodeAt(data, ++outerLoopIndex) === aCode &&
571
+ unsafeCharCodeAt(data, ++outerLoopIndex) === lCode &&
572
+ unsafeCharCodeAt(data, ++outerLoopIndex) === sCode &&
540
573
  unsafeCharCodeAt(data, ++outerLoopIndex) === eCode
541
574
  ) {
542
575
  // @ts-ignore
@@ -559,6 +592,156 @@ export namespace JSON {
559
592
  return schema;
560
593
  }
561
594
 
595
+ // @ts-ignore: Decorator
596
+ @inline function parseMap<T extends Map>(data: string, initializeDefaultValues: boolean): T {
597
+
598
+ const map: nonnull<T> = changetype<nonnull<T>>(
599
+ __new(offsetof<nonnull<T>>(), idof<nonnull<T>>())
600
+ );
601
+
602
+ if (!isDefined(map.set)) {
603
+ return unreachable();
604
+ }
605
+
606
+ const key = Virtual.createEmpty<string>();
607
+ let isKey = false;
608
+ let depth = 0;
609
+ let outerLoopIndex = 1;
610
+ for (; outerLoopIndex < data.length - 1; outerLoopIndex++) {
611
+ const char = unsafeCharCodeAt(data, outerLoopIndex);
612
+ if (char === leftBracketCode) {
613
+ for (
614
+ let arrayValueIndex = outerLoopIndex;
615
+ arrayValueIndex < data.length - 1;
616
+ arrayValueIndex++
617
+ ) {
618
+ const char = unsafeCharCodeAt(data, arrayValueIndex);
619
+ if (char === leftBracketCode) {
620
+ depth++;
621
+ } else if (char === rightBracketCode) {
622
+ depth--;
623
+ if (depth === 0) {
624
+ ++arrayValueIndex;
625
+ map.set(parseMapKey<indexof<T>>(key), JSON.parse<valueof<T>>(data.slice(outerLoopIndex, arrayValueIndex), initializeDefaultValues));
626
+ outerLoopIndex = arrayValueIndex;
627
+ isKey = false;
628
+ break;
629
+ }
630
+ }
631
+ }
632
+ } else if (char === leftBraceCode) {
633
+ for (
634
+ let objectValueIndex = outerLoopIndex;
635
+ objectValueIndex < data.length - 1;
636
+ objectValueIndex++
637
+ ) {
638
+ const char = unsafeCharCodeAt(data, objectValueIndex);
639
+ if (char === leftBraceCode) {
640
+ depth++;
641
+ } else if (char === rightBraceCode) {
642
+ depth--;
643
+ if (depth === 0) {
644
+ ++objectValueIndex;
645
+ map.set(parseMapKey<indexof<T>>(key), JSON.parse<valueof<T>>(data.slice(outerLoopIndex, objectValueIndex), initializeDefaultValues));
646
+ outerLoopIndex = objectValueIndex;
647
+ isKey = false;
648
+ break;
649
+ }
650
+ }
651
+ }
652
+ } else if (char === quoteCode) {
653
+ let escaping = false;
654
+ for (
655
+ let stringValueIndex = ++outerLoopIndex;
656
+ stringValueIndex < data.length - 1;
657
+ stringValueIndex++
658
+ ) {
659
+ const char = unsafeCharCodeAt(data, stringValueIndex);
660
+ if (char === backSlashCode && !escaping) {
661
+ escaping = true;
662
+ } else {
663
+ if (
664
+ char === quoteCode && !escaping
665
+ ) {
666
+ if (isKey === false) {
667
+ key.reinst(data, outerLoopIndex, stringValueIndex);
668
+ isKey = true;
669
+ } else {
670
+ if (isString<valueof<T>>()) {
671
+ map.set(parseMapKey<indexof<T>>(key), data.slice(outerLoopIndex, stringValueIndex));
672
+ }
673
+ isKey = false;
674
+ }
675
+ outerLoopIndex = ++stringValueIndex;
676
+ break;
677
+ }
678
+ escaping = false;
679
+ }
680
+ }
681
+ } else if (
682
+ char == nCode &&
683
+ unsafeCharCodeAt(data, ++outerLoopIndex) === uCode &&
684
+ unsafeCharCodeAt(data, ++outerLoopIndex) === lCode &&
685
+ unsafeCharCodeAt(data, ++outerLoopIndex) === lCode) {
686
+ if (isNullable<valueof<T>>()) {
687
+ map.set(parseMapKey<indexof<T>>(key), null);
688
+ }
689
+ isKey = false;
690
+ } else if (
691
+ char === tCode &&
692
+ unsafeCharCodeAt(data, ++outerLoopIndex) === rCode &&
693
+ unsafeCharCodeAt(data, ++outerLoopIndex) === uCode &&
694
+ unsafeCharCodeAt(data, ++outerLoopIndex) === eCode
695
+ ) {
696
+ if (isBoolean<valueof<T>>()) {
697
+ map.set(parseMapKey<indexof<T>>(key), true);
698
+ }
699
+ isKey = false;
700
+ } else if (
701
+ char === fCode &&
702
+ unsafeCharCodeAt(data, ++outerLoopIndex) === aCode &&
703
+ unsafeCharCodeAt(data, ++outerLoopIndex) === lCode &&
704
+ unsafeCharCodeAt(data, ++outerLoopIndex) === sCode &&
705
+ unsafeCharCodeAt(data, ++outerLoopIndex) === eCode
706
+ ) {
707
+ if (isBoolean<valueof<T>>()) {
708
+ map.set(parseMapKey<indexof<T>>(key), false);
709
+ }
710
+ isKey = false;
711
+ } else if ((char >= 48 && char <= 57) || char === 45) {
712
+ let numberValueIndex = ++outerLoopIndex;
713
+ for (; numberValueIndex < data.length; numberValueIndex++) {
714
+ const char = unsafeCharCodeAt(data, numberValueIndex);
715
+ if (char === colonCode || char === commaCode || char === rightBraceCode || isSpace(char)) {
716
+ if (isFloat<valueof<T>>() || isInteger<valueof<T>>()) {
717
+ map.set(parseMapKey<indexof<T>>(key), parseNumber<valueof<T>>(data.slice(outerLoopIndex - 1, numberValueIndex)));
718
+ }
719
+ outerLoopIndex = numberValueIndex;
720
+ isKey = false;
721
+ break;
722
+ }
723
+ }
724
+ }
725
+ }
726
+
727
+ return map;
728
+ }
729
+
730
+ //@ts-ignore: Decorator
731
+ @inline function parseMapKey<T>(key: Virtual<string>): T {
732
+ const k = key.copyOut();
733
+ if (isString<T>()) {
734
+ return k as T;
735
+ } else if (isBoolean<T>()) {
736
+ // @ts-ignore
737
+ return parseBoolean<T>(k) as T;
738
+ } else if (isInteger<T>() || isFloat<T>()) {
739
+ return parseNumber<T>(k);
740
+ }
741
+
742
+ throw new Error(`JSON: Cannot parse JSON object to a Map with a key of type ${nameof<T>()}`);
743
+ }
744
+
562
745
  // @ts-ignore: Decorator
563
746
  @inline function parseArray<T extends unknown[]>(data: string): T {
564
747
  if (isString<valueof<T>>()) {
@@ -572,7 +755,8 @@ export namespace JSON {
572
755
  } else if (isArrayLike<valueof<T>>()) {
573
756
  // @ts-ignore
574
757
  return parseArrayArray<T>(data);
575
- // @ts-ignore
758
+ } else if (isMap<valueof<T>>()) {
759
+ return parseObjectArray<T>(data);
576
760
  } else if (isManaged<valueof<T>>() || isReference<valueof<T>>()) {
577
761
  // We instantiate the required memory for the class and fill it. This is extremely unsafe and uses "a bit of magic".
578
762
  const type = changetype<nonnull<valueof<T>>>(
@@ -580,11 +764,10 @@ export namespace JSON {
580
764
  );
581
765
  // @ts-ignore
582
766
  if (isDefined(type.__JSON_Set_Key)) {
583
- // @ts-ignore
584
767
  return parseObjectArray<T>(data);
585
768
  }
586
- return unreachable();
587
769
  }
770
+
588
771
  return unreachable();
589
772
  }
590
773
 
@@ -725,3 +908,9 @@ function parseDate(dateTimeString: string): Date {
725
908
  // is globally aliased to wasi_Date (or some other superclass).
726
909
  return new Date(d.getTime());
727
910
  }
911
+
912
+ // @ts-ignore: Decorator
913
+ @inline function isMap<T>(): bool {
914
+ let type = changetype<T>(0);
915
+ return type instanceof Map;
916
+ }
package/assembly/test.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import { JSON } from "./src/json";
2
2
 
3
3
  // @ts-ignore
4
-
4
+ @serializable
5
5
  class Vec3 {
6
6
  x: f64 = 3.4;
7
7
  y: f64 = 1.2;
@@ -9,8 +9,9 @@ class Vec3 {
9
9
  }
10
10
 
11
11
  // @ts-ignore
12
- @json
12
+ @serializable
13
13
  class Player extends Vec3 {
14
+ @alias("first name")
14
15
  firstName: string;
15
16
  lastName: string;
16
17
  lastActive: Date;
@@ -47,7 +48,7 @@ console.log("Implemented: " + JSON.stringify(JSON.parse<Vec3>('{}', true)));
47
48
 
48
49
  console.log("Original: " + JSON.stringify(player));
49
50
  //console.log("Revised: " + vec.__JSON_Deserialize('{"x":3,"y":1,"z":8}').__JSON_Serialize());
50
- console.log("Implemented: " + JSON.stringify(JSON.parse<Player>('{"firstName":"Emmet","lastName":"West","lastActive":"2023-11-16T04:06:35.108285303Z","age":23,"pos":{"x":3.4,"y":1.2,"z":8.3},"isVerified":true,"x":5","y":4","z":3}')));
51
+ console.log("Implemented: " + JSON.stringify(JSON.parse<Player>('{"first name":"Emmet","lastName":"West","lastActive":"2023-11-16T04:06:35.108285303Z","age":23,"pos":{"x":3.4,"y":1.2,"z":8.3},"isVerified":true,"x":5","y":4","z":3}')));
51
52
 
52
53
  @serializable
53
54
  class Wrapper<T> {
@@ -56,6 +57,7 @@ class Wrapper<T> {
56
57
 
57
58
  @serializable
58
59
  class Foo {
60
+ @alias("ur mom")
59
61
  foo!: string;
60
62
  }
61
63
 
@@ -70,7 +72,7 @@ const foo: Wrapper<Foo> = {
70
72
 
71
73
  foo.data.foo = "ha";
72
74
  console.log(JSON.stringify(foo));
73
- console.log(JSON.stringify(JSON.parse<Wrapper<Foo>>("{\"data\":{\"foo\":\"ha\"}}")))
75
+ console.log(JSON.stringify(JSON.parse<Wrapper<Foo>>("{\"data\":{\"ur mom\":\"ha\"}}")))
74
76
  /*
75
77
  // 9,325,755
76
78
  bench("Stringify Object (Vec3)", () => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "json-as",
3
- "version": "0.6.6",
3
+ "version": "0.7.1",
4
4
  "description": "JSON encoder/decoder for AssemblyScript",
5
5
  "types": "assembly/index.ts",
6
6
  "author": "Jairus Tanaka",
@@ -10,7 +10,8 @@
10
10
  "Derek Barrera",
11
11
  "Frankk Taylor",
12
12
  "lekiano",
13
- "Florian Guitton"
13
+ "Florian Guitton",
14
+ "Matt Johnson-Pint"
14
15
  ],
15
16
  "license": "MIT",
16
17
  "scripts": {
@@ -28,18 +29,18 @@
28
29
  "prettier": "as-prettier -w ."
29
30
  },
30
31
  "devDependencies": {
31
- "@as-pect/cli": "^8.0.1",
32
- "@as-tral/cli": "^2.0.1",
32
+ "@as-pect/cli": "^8.1.0",
33
+ "@as-tral/cli": "^3.0.2",
33
34
  "@assemblyscript/wasi-shim": "^0.1.0",
34
35
  "as-bench": "^0.0.0-alpha",
35
- "assemblyscript": "^0.27.18",
36
+ "assemblyscript": "^0.27.22",
36
37
  "assemblyscript-prettier": "^3.0.1",
37
38
  "benchmark": "^2.1.4",
38
39
  "kati": "^0.6.2",
39
40
  "microtime": "^3.1.1",
40
- "prettier": "^3.1.0",
41
+ "prettier": "^3.1.1",
41
42
  "tinybench": "^2.5.1",
42
- "typescript": "^5.2.2",
43
+ "typescript": "^5.3.3",
43
44
  "visitor-as": "^0.11.4"
44
45
  },
45
46
  "dependencies": {
@@ -47,6 +48,9 @@
47
48
  "as-variant": "^0.4.1",
48
49
  "as-virtual": "^0.1.9"
49
50
  },
51
+ "overrides": {
52
+ "assemblyscript": "$assemblyscript"
53
+ },
50
54
  "repository": {
51
55
  "type": "git",
52
56
  "url": "git+https://github.com/JairusSW/as-json.git"
@@ -21,7 +21,7 @@ class AsJSONTransform extends BaseVisitor {
21
21
  }
22
22
  visitMethodDeclaration() { }
23
23
  visitClassDeclaration(node) {
24
- var _c;
24
+ var _c, _d;
25
25
  const className = node.name.text;
26
26
  if (!((_c = node.decorators) === null || _c === void 0 ? void 0 : _c.length))
27
27
  return;
@@ -76,6 +76,12 @@ class AsJSONTransform extends BaseVisitor {
76
76
  // @ts-ignore
77
77
  let type = toString(member.type);
78
78
  const name = member.name.text;
79
+ let aliasName = name;
80
+ if (member.decorators && ((_d = member.decorators[0]) === null || _d === void 0 ? void 0 : _d.name.text) === "alias") {
81
+ if (member.decorators[0] && member.decorators[0].args[0]) {
82
+ aliasName = member.decorators[0].args[0].value;
83
+ }
84
+ }
79
85
  this.currentClass.keys.push(name);
80
86
  // @ts-ignore
81
87
  this.currentClass.types.push(type);
@@ -90,9 +96,9 @@ class AsJSONTransform extends BaseVisitor {
90
96
  "u64",
91
97
  "i64",
92
98
  ].includes(type.toLowerCase())) {
93
- this.currentClass.encodeStmts.push(`"${name}":\${this.${name}.toString()},`);
99
+ this.currentClass.encodeStmts.push(`"${aliasName}":\${this.${name}.toString()},`);
94
100
  // @ts-ignore
95
- this.currentClass.setDataStmts.push(`if (key.equals("${name}")) {
101
+ this.currentClass.setDataStmts.push(`if (key.equals("${aliasName}")) {
96
102
  this.${name} = __atoi_fast<${type}>(data, val_start << 1, val_end << 1);
97
103
  return;
98
104
  }
@@ -106,9 +112,9 @@ class AsJSONTransform extends BaseVisitor {
106
112
  "f32",
107
113
  "f64",
108
114
  ].includes(type.toLowerCase())) {
109
- this.currentClass.encodeStmts.push(`"${name}":\${this.${name}.toString()},`);
115
+ this.currentClass.encodeStmts.push(`"${aliasName}":\${this.${name}.toString()},`);
110
116
  // @ts-ignore
111
- this.currentClass.setDataStmts.push(`if (key.equals("${name}")) {
117
+ this.currentClass.setDataStmts.push(`if (key.equals("${aliasName}")) {
112
118
  this.${name} = __parseObjectValue<${type}>(data.slice(val_start, val_end), initializeDefaultValues);
113
119
  return;
114
120
  }
@@ -118,9 +124,9 @@ class AsJSONTransform extends BaseVisitor {
118
124
  }
119
125
  }
120
126
  else {
121
- this.currentClass.encodeStmts.push(`"${name}":\${JSON.stringify<${type}>(this.${name})},`);
127
+ this.currentClass.encodeStmts.push(`"${aliasName}":\${JSON.stringify<${type}>(this.${name})},`);
122
128
  // @ts-ignore
123
- this.currentClass.setDataStmts.push(`if (key.equals("${name}")) {
129
+ this.currentClass.setDataStmts.push(`if (key.equals("${aliasName}")) {
124
130
  this.${name} = __parseObjectValue<${type}>(val_start ? data.slice(val_start, val_end) : data, initializeDefaultValues);
125
131
  return;
126
132
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@json-as/transform",
3
- "version": "0.6.6",
3
+ "version": "0.7.0",
4
4
  "description": "JSON encoder/decoder for AssemblyScript",
5
5
  "main": "./lib/index.js",
6
6
  "author": "Jairus Tanaka",
@@ -88,6 +88,12 @@ class AsJSONTransform extends BaseVisitor {
88
88
  let type = toString(member.type);
89
89
 
90
90
  const name = member.name.text;
91
+ let aliasName = name;
92
+ if (member.decorators && member.decorators[0]?.name.text === "alias") {
93
+ if (member.decorators[0] && member.decorators[0].args![0]) {
94
+ aliasName = member.decorators[0].args![0].value;
95
+ }
96
+ }
91
97
  this.currentClass.keys.push(name);
92
98
  // @ts-ignore
93
99
  this.currentClass.types.push(type);
@@ -105,11 +111,11 @@ class AsJSONTransform extends BaseVisitor {
105
111
  ].includes(type.toLowerCase())
106
112
  ) {
107
113
  this.currentClass.encodeStmts.push(
108
- `"${name}":\${this.${name}.toString()},`
114
+ `"${aliasName}":\${this.${name}.toString()},`
109
115
  );
110
116
  // @ts-ignore
111
117
  this.currentClass.setDataStmts.push(
112
- `if (key.equals("${name}")) {
118
+ `if (key.equals("${aliasName}")) {
113
119
  this.${name} = __atoi_fast<${type}>(data, val_start << 1, val_end << 1);
114
120
  return;
115
121
  }
@@ -128,11 +134,11 @@ class AsJSONTransform extends BaseVisitor {
128
134
  ].includes(type.toLowerCase())
129
135
  ) {
130
136
  this.currentClass.encodeStmts.push(
131
- `"${name}":\${this.${name}.toString()},`
137
+ `"${aliasName}":\${this.${name}.toString()},`
132
138
  );
133
139
  // @ts-ignore
134
140
  this.currentClass.setDataStmts.push(
135
- `if (key.equals("${name}")) {
141
+ `if (key.equals("${aliasName}")) {
136
142
  this.${name} = __parseObjectValue<${type}>(data.slice(val_start, val_end), initializeDefaultValues);
137
143
  return;
138
144
  }
@@ -145,11 +151,11 @@ class AsJSONTransform extends BaseVisitor {
145
151
  }
146
152
  } else {
147
153
  this.currentClass.encodeStmts.push(
148
- `"${name}":\${JSON.stringify<${type}>(this.${name})},`
154
+ `"${aliasName}":\${JSON.stringify<${type}>(this.${name})},`
149
155
  );
150
156
  // @ts-ignore
151
157
  this.currentClass.setDataStmts.push(
152
- `if (key.equals("${name}")) {
158
+ `if (key.equals("${aliasName}")) {
153
159
  this.${name} = __parseObjectValue<${type}>(val_start ? data.slice(val_start, val_end) : data, initializeDefaultValues);
154
160
  return;
155
161
  }