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 +1 -0
- package/assembly/__tests__/as-json.spec.ts +145 -1
- package/assembly/src/chars.ts +8 -0
- package/assembly/src/json.ts +230 -41
- package/assembly/test.ts +6 -4
- package/package.json +11 -7
- package/transform/lib/index.js +13 -7
- package/transform/package.json +1 -1
- package/transform/src/index.ts +12 -6
package/README.md
CHANGED
|
@@ -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
|
+
});
|
package/assembly/src/chars.ts
CHANGED
|
@@ -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;
|
package/assembly/src/json.ts
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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 ?
|
|
145
|
+
out = data ? trueWord : falseWord;
|
|
121
146
|
return;
|
|
122
147
|
} else if (isNullable<T>() && data == null) {
|
|
123
|
-
out =
|
|
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 =
|
|
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
|
-
|
|
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
|
-
|
|
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)
|
|
348
|
-
|
|
349
|
-
|
|
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)
|
|
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(
|
|
420
|
-
else if (data.length > 4 && data.startsWith(
|
|
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
|
-
|
|
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
|
-
|
|
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 (
|
|
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) ===
|
|
538
|
-
unsafeCharCodeAt(data, ++outerLoopIndex) ===
|
|
539
|
-
unsafeCharCodeAt(data, ++outerLoopIndex) ===
|
|
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
|
-
|
|
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
|
-
@
|
|
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>('{"
|
|
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\":{\"
|
|
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.
|
|
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
|
|
32
|
-
"@as-tral/cli": "^
|
|
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.
|
|
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.
|
|
41
|
+
"prettier": "^3.1.1",
|
|
41
42
|
"tinybench": "^2.5.1",
|
|
42
|
-
"typescript": "^5.
|
|
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"
|
package/transform/lib/index.js
CHANGED
|
@@ -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(`"${
|
|
99
|
+
this.currentClass.encodeStmts.push(`"${aliasName}":\${this.${name}.toString()},`);
|
|
94
100
|
// @ts-ignore
|
|
95
|
-
this.currentClass.setDataStmts.push(`if (key.equals("${
|
|
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(`"${
|
|
115
|
+
this.currentClass.encodeStmts.push(`"${aliasName}":\${this.${name}.toString()},`);
|
|
110
116
|
// @ts-ignore
|
|
111
|
-
this.currentClass.setDataStmts.push(`if (key.equals("${
|
|
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(`"${
|
|
127
|
+
this.currentClass.encodeStmts.push(`"${aliasName}":\${JSON.stringify<${type}>(this.${name})},`);
|
|
122
128
|
// @ts-ignore
|
|
123
|
-
this.currentClass.setDataStmts.push(`if (key.equals("${
|
|
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
|
}
|
package/transform/package.json
CHANGED
package/transform/src/index.ts
CHANGED
|
@@ -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
|
-
`"${
|
|
114
|
+
`"${aliasName}":\${this.${name}.toString()},`
|
|
109
115
|
);
|
|
110
116
|
// @ts-ignore
|
|
111
117
|
this.currentClass.setDataStmts.push(
|
|
112
|
-
`if (key.equals("${
|
|
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
|
-
`"${
|
|
137
|
+
`"${aliasName}":\${this.${name}.toString()},`
|
|
132
138
|
);
|
|
133
139
|
// @ts-ignore
|
|
134
140
|
this.currentClass.setDataStmts.push(
|
|
135
|
-
`if (key.equals("${
|
|
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
|
-
`"${
|
|
154
|
+
`"${aliasName}":\${JSON.stringify<${type}>(this.${name})},`
|
|
149
155
|
);
|
|
150
156
|
// @ts-ignore
|
|
151
157
|
this.currentClass.setDataStmts.push(
|
|
152
|
-
`if (key.equals("${
|
|
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
|
}
|