json-as 0.8.3 → 0.8.5

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/CHANGELOG CHANGED
@@ -1,2 +1,3 @@
1
1
  v0.8.2 - Properties starting with `static` or `private` would be ignored
2
- v0.8.3 - Dirty fix to issue #68. Add __JSON_Stringify callable to global scope.
2
+ v0.8.3 - Dirty fix to issue #68. Add __JSON_Stringify callable to global scope.
3
+ v0.8.4 - Fix #71. Classes with the extending class overriding a property cause the property to be serialized twice.
package/README.md CHANGED
@@ -7,7 +7,7 @@
7
7
  ██║ ██║███████║ ╚█████╔╝███████║╚██████╔╝██║ ╚████║
8
8
  ╚═╝ ╚═╝╚══════╝ ╚════╝ ╚══════╝ ╚═════╝ ╚═╝ ╚═══╝
9
9
 
10
- v0.8.3
10
+ v0.8.5
11
11
  </pre>
12
12
  </h3>
13
13
 
@@ -80,6 +80,43 @@ const parsed = JSON.parse<Player>(stringified);
80
80
 
81
81
  If you use this project in your codebase, consider dropping a [star](https://github.com/JairusSW/as-json). I would really appreciate it!
82
82
 
83
+ ## Performance
84
+
85
+ Run or view the benchmarks [here](https://github.com/JairusSW/as-json/tree/master/bench)
86
+
87
+ Below are benchmark results comparing JavaScript's built-in JSON implementation and `JSON-AS`
88
+
89
+ My library beats JSON (written in C++) on all counts *and*, I see many places where I can pull at least a 60% uplift in performance if I implement it.
90
+
91
+
92
+ Serialization Benchmarks:
93
+
94
+ | Value | JavaScript (ops/s) | JSON-as (ops/s) | JSON-AS with Pages |
95
+ |----------------------------|--------------------|-----------------|--------------------|
96
+ | "hello world" | 28,629,598 | 64,210,666 | + 124% |
97
+ | 12345 | 31,562,431 | 56,329,066 | 321,783,941 ops/s |
98
+ | 1.2345 | 15,977,278 | 20,322,939 | 30,307,616 ops/s |
99
+ | [[],[[]],[[],[[]]]] | 8,998,624 | 34,453,102 | + 283% |
100
+
101
+
102
+
103
+ Deserialization Benchmarks: (WIP)
104
+
105
+ | Value | JavaScript (ops/s) | JSON-AS (ops/s) | % Diff |
106
+ |----------------------------|--------------------|-----------------|--------|
107
+ | "12345" | 34,647,886 | 254,640,930 | + 635% |
108
+
109
+
110
+ And my PC specs:
111
+
112
+ | Component | Specification |
113
+ |-----------------|--------------------------------------|
114
+ | Wasmer Version | v4.3.0 |
115
+ | CPU | AMD Ryzen 7 7800x3D @ 6.00 GHz |
116
+ | Memory | T-Force DDR5 6000 MHz |
117
+ | OS | Ubuntu WSL2 |
118
+ | Graphics | AMD Radeon RX 6750XT |
119
+
83
120
  ## Issues
84
121
 
85
122
  Please submit an issue to https://github.com/JairusSW/as-json/issues if you find anything wrong with this library
@@ -14,7 +14,32 @@ function canSer<T>(data: T, toBe: string): void {
14
14
  const serialized = JSON.stringify<T>(data);
15
15
  expect(serialized).toBe(toBe);
16
16
  }
17
+ @json
18
+ class BaseObject {
19
+ a: string;
20
+
21
+ constructor(a: string) {
22
+ this.a = a;
23
+ }
24
+ }
25
+
26
+ @json
27
+ class DerivedObject extends BaseObject {
28
+ b: string;
29
+
30
+ constructor(a: string, b: string) {
31
+ super(a);
32
+ this.b = b;
33
+ }
34
+ }
17
35
 
36
+ describe("Ser/de object hierarchies", () => {
37
+ it("should ser/de objects derived from base objects", () => {
38
+ const o = new DerivedObject("1", "2");
39
+ const s = '{"a":"1","b":"2"}';
40
+ canSerde(o, s);
41
+ });
42
+ });
18
43
  @json
19
44
  class Map4 {
20
45
  a: string;
@@ -12,3 +12,13 @@ declare function serializable(target: any): void;
12
12
  * Property decorator that provides an alias name for JSON serialization.
13
13
  */
14
14
  declare function alias(name: string): Function;
15
+
16
+ /**
17
+ * Property decorator that allows a field to be omitted when equal to an Expression.
18
+ */
19
+ declare function omitwhen(condition: string): Function;
20
+
21
+ /**
22
+ * Property decorator that allows a field to be omitted when a property is null.
23
+ */
24
+ declare function omitnull(): Function;
@@ -42,6 +42,7 @@ import {
42
42
  } from "./chars";
43
43
  import { snip_fast, unsafeCharCodeAt, containsCodePoint } from "./util";
44
44
  import { Virtual } from "as-virtual/assembly";
45
+ import { Box } from "as-container/assembly";
45
46
 
46
47
  /**
47
48
  * JSON Encoder/Decoder for AssemblyScript
@@ -62,7 +63,12 @@ export namespace JSON {
62
63
  return serializeString(data as string);
63
64
  } else if (isBoolean<T>()) {
64
65
  return data ? "true" : "false";
65
- } else if (isNullable<T>() && data == null) {
66
+ } else if (data instanceof Box) {
67
+ if (isNullable<T>() && (changetype<usize>(data._val) == <usize>0 || changetype<usize>(data) == <usize>0)) {
68
+ return nullWord;
69
+ }
70
+ return JSON.stringify(data.unwrap());
71
+ } else if (isNullable<T>() && changetype<usize>(data) == <usize>0) {
66
72
  return nullWord;
67
73
  // @ts-ignore
68
74
  } else if ((isInteger<T>() || isFloat<T>()) && isFinite(data)) {
@@ -126,7 +132,7 @@ export namespace JSON {
126
132
  result.write(serializeString(unchecked(keys[end]).toString()));
127
133
  result.writeCodePoint(colonCode);
128
134
  result.write(__JSON_Stringify(unchecked(values[end])));
129
-
135
+
130
136
  result.writeCodePoint(rightBraceCode);
131
137
  return result.toString();
132
138
  } else {
@@ -135,86 +141,10 @@ export namespace JSON {
135
141
  );
136
142
  }
137
143
  }
138
- /**
139
- * Stringifies valid JSON data.
140
- * ```js
141
- * __JSON_Stringify<T>(data)
142
- * ```
143
- * @param data T
144
- * @returns string
145
- */
146
144
  // @ts-ignore: Decorator
145
+ @unsafe
147
146
  @inline export function stringifyTo<T>(data: T, out: string): void {
148
- // String
149
- if (isString<T>() && data != null) {
150
- out = serializeString(data as string);
151
- return;
152
- } else if (isBoolean<T>()) {
153
- out = data ? trueWord : falseWord;
154
- return;
155
- } else if (isNullable<T>() && data == null) {
156
- out = nullWord;
157
- return;
158
- // @ts-ignore
159
- } else if ((isInteger<T>() || isFloat<T>()) && isFinite(data)) {
160
- // @ts-ignore
161
- out = data.toString();
162
- return;
163
- // @ts-ignore: Hidden function
164
- } else if (isDefined(data.__JSON_Serialize)) {
165
- // @ts-ignore: Hidden function
166
- out = data.__JSON_Serialize();
167
- return;
168
- } else if (data instanceof Date) {
169
- out = quoteWord + data.toISOString() + quoteWord;
170
- return;
171
- } else if (isArrayLike<T>()) {
172
- // @ts-ignore
173
- if (data.length == 0) {
174
- out = emptyArrayWord;
175
- return;
176
- // @ts-ignore
177
- } else if (isString<valueof<T>>()) {
178
- out = leftBracketWord;
179
- // @ts-ignore
180
- for (let i = 0; i < data.length - 1; i++) {
181
- // @ts-ignore
182
- out += serializeString(unchecked(data[i]));
183
- out += commaWord;
184
- }
185
- // @ts-ignore
186
- out += serializeString(unchecked(data[data.length - 1]));
187
- out += rightBracketWord;
188
- return;
189
- // @ts-ignore
190
- } else if (isBoolean<valueof<T>>()) {
191
- // @ts-ignore
192
- out = leftBracketWord + data.join(commaWord) + rightBracketWord;
193
- return;
194
- // @ts-ignore
195
- } else if (isFloat<valueof<T>>() || isInteger<valueof<T>>()) {
196
- // @ts-ignore
197
- out = leftBracketWord + data.join(commaWord) + rightBracketWord;
198
- return;
199
- } else {
200
- let result = new StringSink(leftBracketWord);
201
- // @ts-ignore
202
- for (let i = 0; i < data.length - 1; i++) {
203
- // @ts-ignore
204
- result.write(__JSON_Stringify(unchecked(data[i])));
205
- result.writeCodePoint(commaCode);
206
- }
207
- // @ts-ignore
208
- result.write(__JSON_Stringify(unchecked(data[data.length - 1])));
209
- result.writeCodePoint(rightBracketCode);
210
- out = result.toString();
211
- return;
212
- }
213
- } else {
214
- throw new Error(
215
- `Could not serialize data of type ${nameof<T>()}. Make sure to add the correct decorators to classes.`
216
- );
217
- }
147
+ throw new Error("Method is deprecated");
218
148
  }
219
149
  /**
220
150
  * Parses valid JSON strings into their original format.
@@ -227,7 +157,6 @@ export namespace JSON {
227
157
 
228
158
  // @ts-ignore: Decorator
229
159
  @inline export function parse<T>(data: string, initializeDefaultValues: boolean = false): T {
230
- let type: T;
231
160
  if (isString<T>()) {
232
161
  // @ts-ignore
233
162
  return parseString(data);
@@ -239,6 +168,15 @@ export namespace JSON {
239
168
  } else if (isArrayLike<T>()) {
240
169
  // @ts-ignore
241
170
  return parseArray<T>(data.trimStart());
171
+ // @ts-ignore
172
+ }
173
+ let type: nonnull<T> = changetype<nonnull<T>>(0);
174
+ if (type instanceof Box) {
175
+ const instance = changetype<nonnull<T>>(__new(offsetof<nonnull<T>>(), idof<nonnull<T>>()))// as Box<usize>;
176
+ const val = instance._val;
177
+ instance._val = parseDirectInference(val, data);
178
+ // @ts-ignore
179
+ return changetype<T>(instance);
242
180
  } else if (isNullable<T>() && data == nullWord) {
243
181
  // @ts-ignore
244
182
  return null;
@@ -260,7 +198,6 @@ export namespace JSON {
260
198
 
261
199
  // @ts-ignore: Decorator
262
200
  @global @inline function __parseObjectValue<T>(data: string, initializeDefaultValues: boolean): T {
263
- let type: T;
264
201
  if (isString<T>()) {
265
202
  // @ts-ignore
266
203
  return data;
@@ -272,6 +209,15 @@ export namespace JSON {
272
209
  } else if (isArrayLike<T>()) {
273
210
  // @ts-ignore
274
211
  return parseArray<T>(data);
212
+ // @ts-ignore
213
+ }
214
+ let type: nonnull<T> = changetype<nonnull<T>>(0);
215
+ if (type instanceof Box) {
216
+ const instance = changetype<nonnull<T>>(__new(offsetof<nonnull<T>>(), idof<nonnull<T>>()))// as Box<usize>;
217
+ const val = instance._val;
218
+ instance._val = parseDirectInference(val, data);
219
+ // @ts-ignore
220
+ return changetype<T>(instance);
275
221
  } else if (isNullable<T>() && data == nullWord) {
276
222
  // @ts-ignore
277
223
  return null;
@@ -442,6 +388,18 @@ export namespace JSON {
442
388
  else if (type instanceof f32) return f32.parse(data);
443
389
  }
444
390
 
391
+ // @ts-ignore: Decorator
392
+ @inline function parseNumberDirectInference<T>(type: T, data: string): T {
393
+ if (isInteger(type)) {
394
+ // @ts-ignore
395
+ return snip_fast<T>(data);
396
+ }
397
+ // @ts-ignore
398
+ if (type instanceof f64) return f64.parse(data);
399
+ // @ts-ignore
400
+ else if (type instanceof f32) return f32.parse(data);
401
+ }
402
+
445
403
  // @ts-ignore: Decorator
446
404
  @inline function parseObject<T>(data: string, initializeDefaultValues: boolean): T {
447
405
  const schema: nonnull<T> = changetype<nonnull<T>>(
@@ -584,7 +542,7 @@ export namespace JSON {
584
542
  );
585
543
 
586
544
  if (!isDefined(map.set)) {
587
- return unreachable();
545
+ throw new Error("Tried to parse a map, but the types did not match!")
588
546
  }
589
547
 
590
548
  const key = Virtual.createEmpty<string>();
@@ -758,7 +716,7 @@ export namespace JSON {
758
716
  }
759
717
  }
760
718
 
761
- return unreachable();
719
+ throw new Error("Tried to parse array, but failed!")
762
720
  }
763
721
 
764
722
  // @ts-ignore: Decorator
@@ -892,14 +850,14 @@ function parseDate(dateTimeString: string): Date {
892
850
  }
893
851
 
894
852
  // Dirty fix
895
- // @ts-ignore: Decorator
853
+ // @ts-ignore: Decorator
896
854
  @global @inline function __JSON_Stringify<T>(data: T): string {
897
855
  // String
898
856
  if (isString<T>() && data != null) {
899
857
  return serializeString(data as string);
900
858
  } else if (isBoolean<T>()) {
901
859
  return data ? "true" : "false";
902
- } else if (isNullable<T>() && data == null) {
860
+ } else if (isNullable<T>() && changetype<usize>(data) == <usize>0) {
903
861
  return nullWord;
904
862
  // @ts-ignore
905
863
  } else if ((isInteger<T>() || isFloat<T>()) && isFinite(data)) {
@@ -911,6 +869,11 @@ function parseDate(dateTimeString: string): Date {
911
869
  return data.__JSON_Serialize();
912
870
  } else if (data instanceof Date) {
913
871
  return `"${data.toISOString()}"`;
872
+ } else if (data instanceof Box) {
873
+ if (isNullable<T>() && changetype<usize>(data) == <usize>0) {
874
+ return nullWord;
875
+ }
876
+ return JSON.stringify(data.unwrap());
914
877
  } else if (isArrayLike<T>()) {
915
878
  // @ts-ignore
916
879
  if (data.length == 0) {
@@ -963,7 +926,7 @@ function parseDate(dateTimeString: string): Date {
963
926
  result.write(serializeString(unchecked(keys[end]).toString()));
964
927
  result.writeCodePoint(colonCode);
965
928
  result.write(__JSON_Stringify(unchecked(values[end])));
966
-
929
+
967
930
  result.writeCodePoint(rightBraceCode);
968
931
  return result.toString();
969
932
  } else {
@@ -971,4 +934,8 @@ function parseDate(dateTimeString: string): Date {
971
934
  `Could not serialize data of type ${nameof<T>()}. Make sure to add the correct decorators to classes.`
972
935
  );
973
936
  }
937
+ }
938
+
939
+ @inline function parseDirectInference<T>(type: T, data: string, initializeDefaultValues: boolean = false): T {
940
+ return JSON.parse<T>(data, initializeDefaultValues)
974
941
  }
package/assembly/test.ts CHANGED
@@ -1,7 +1,23 @@
1
+ import { Box } from "as-container";
1
2
  import { JSON } from "./src/json";
2
3
 
3
- console.log(JSON.stringify(JSON.parse<f64[]>(`[
4
- 1,
5
- 2,
6
- 3
7
- ]`)));
4
+ @json
5
+ class Foo {
6
+ @omitnull
7
+ optionalNumber: Box<i32> | null = null;
8
+ @omitif("this.tristateValue!.unwrap() == false")
9
+ tristateValue: Box<bool> | null = null;
10
+ }
11
+
12
+ const foo: Foo = {
13
+ optionalNumber: null,
14
+ tristateValue: Box.from(true)
15
+ }
16
+
17
+ console.log(JSON.stringify(foo));
18
+
19
+ const p1 = JSON.parse<Box<i32> | null>("null");
20
+ console.log(JSON.stringify<Box<i32> | null>(p1));
21
+ console.log(changetype<usize>(p1).toString())
22
+ const p2 = JSON.parse<Foo>("{\"optionalNumber\":null,\"tristateValue\":false}");
23
+ console.log(JSON.stringify(p2));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "json-as",
3
- "version": "0.8.3",
3
+ "version": "0.8.5",
4
4
  "description": "JSON encoder/decoder for AssemblyScript",
5
5
  "types": "assembly/index.ts",
6
6
  "author": "Jairus Tanaka",
@@ -44,9 +44,10 @@
44
44
  "visitor-as": "^0.11.4"
45
45
  },
46
46
  "dependencies": {
47
+ "as-container": "^0.8.0",
47
48
  "as-string-sink": "^0.5.3",
48
49
  "as-variant": "^0.4.1",
49
- "as-virtual": "^0.1.9"
50
+ "as-virtual": "^0.2.0"
50
51
  },
51
52
  "overrides": {
52
53
  "assemblyscript": "$assemblyscript"
@@ -22,7 +22,7 @@ class AsJSONTransform extends BaseVisitor {
22
22
  }
23
23
  visitMethodDeclaration() { }
24
24
  visitClassDeclaration(node) {
25
- var _c, _d;
25
+ var _c, _d, _e, _f, _g, _h, _j, _k, _l;
26
26
  const className = node.name.text;
27
27
  if (!((_c = node.decorators) === null || _c === void 0 ? void 0 : _c.length))
28
28
  return;
@@ -53,94 +53,123 @@ class AsJSONTransform extends BaseVisitor {
53
53
  setDataStmts: [],
54
54
  initializeStmts: []
55
55
  };
56
- if (this.currentClass.parent.length > 0) {
56
+ if (this.currentClass.parent.length) {
57
57
  const parentSchema = this.schemasList.find((v) => v.name == this.currentClass.parent);
58
58
  if (parentSchema === null || parentSchema === void 0 ? void 0 : parentSchema.encodeStmts) {
59
59
  parentSchema === null || parentSchema === void 0 ? void 0 : parentSchema.encodeStmts.push((parentSchema === null || parentSchema === void 0 ? void 0 : parentSchema.encodeStmts.pop()) + ",");
60
- this.currentClass.encodeStmts.push(...parentSchema === null || parentSchema === void 0 ? void 0 : parentSchema.encodeStmts);
60
+ for (let i = 0; i < parentSchema.keys.length; i++) {
61
+ const key = parentSchema.keys[i];
62
+ if (node.members.filter(v => v.name.text == key) == undefined)
63
+ this.currentClass.encodeStmts.unshift(parentSchema.encodeStmts[i]);
64
+ }
61
65
  }
62
66
  }
63
67
  const parentSchema = this.schemasList.find((v) => v.name == this.currentClass.parent);
64
68
  const members = [
65
- ...node.members,
66
- ...(parentSchema ? parentSchema.node.members : []),
69
+ ...node.members
67
70
  ];
71
+ if (parentSchema) {
72
+ for (const mem of parentSchema.node.members) {
73
+ if (members.find(v => v.name === mem.name) == undefined)
74
+ members.unshift(mem);
75
+ }
76
+ }
68
77
  for (const mem of members) {
69
78
  // @ts-ignore
70
79
  if (mem.type && mem.type.name && mem.type.name.identifier.text) {
71
80
  const member = mem;
72
81
  const lineText = toString(member);
73
82
  //console.log("Member: " + lineText)
74
- if (!lineText.startsWith("private ") && !lineText.startsWith("static ")) {
75
- // @ts-ignore
76
- let type = toString(member.type);
77
- const name = member.name.text;
78
- let aliasName = name;
79
- // @ts-ignore
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
- // @ts-ignore
83
- aliasName = member.decorators[0].args[0].value;
84
- }
83
+ if (lineText.startsWith("private ") && lineText.startsWith("static "))
84
+ continue;
85
+ const omitnull = (_d = member.decorators) === null || _d === void 0 ? void 0 : _d.find(v => v.name.text == "omitnull");
86
+ console.log((_e = member.decorators) === null || _e === void 0 ? void 0 : _e.find(v => v.name.text === "omitif"));
87
+ const omitif = (_k = (_j = (_h = (_g = (_f = member.decorators) === null || _f === void 0 ? void 0 : _f.find(v => v.name.text === "omitif")) === null || _g === void 0 ? void 0 : _g.args) === null || _h === void 0 ? void 0 : _h[0]) === null || _j === void 0 ? void 0 : _j.value) !== null && _k !== void 0 ? _k : null;
88
+ // @ts-ignore
89
+ let type = toString(member.type);
90
+ const name = member.name.text;
91
+ let aliasName = name;
92
+ // @ts-ignore
93
+ if (member.decorators && ((_l = member.decorators[0]) === null || _l === void 0 ? void 0 : _l.name.text) === "alias") {
94
+ if (member.decorators[0] && member.decorators[0].args[0]) {
95
+ // @ts-ignore
96
+ aliasName = member.decorators[0].args[0].value;
97
+ }
98
+ }
99
+ this.currentClass.keys.push(name);
100
+ // @ts-ignore
101
+ this.currentClass.types.push(type);
102
+ // @ts-ignore
103
+ if ([
104
+ "u8",
105
+ "i8",
106
+ "u16",
107
+ "i16",
108
+ "u32",
109
+ "i32",
110
+ "u64",
111
+ "i64",
112
+ ].includes(type.toLowerCase())) {
113
+ if (omitif) {
114
+ this.currentClass.encodeStmts.push(`\${${omitif} ? "" : \`,${encodeKey(aliasName)}:\${this.${name}}\`}`);
115
+ }
116
+ else {
117
+ this.currentClass.encodeStmts.push(`,${encodeKey(aliasName)}:\${this.${name}}`);
85
118
  }
86
- this.currentClass.keys.push(name);
87
- // @ts-ignore
88
- this.currentClass.types.push(type);
89
119
  // @ts-ignore
90
- if ([
91
- "u8",
92
- "i8",
93
- "u16",
94
- "i16",
95
- "u32",
96
- "i32",
97
- "u64",
98
- "i64",
99
- ].includes(type.toLowerCase())) {
100
- this.currentClass.encodeStmts.push(`${encodeKey(aliasName)}:\${this.${name}},`);
101
- // @ts-ignore
102
- this.currentClass.setDataStmts.push(`if (key.equals(${JSON.stringify(aliasName)})) {
120
+ this.currentClass.setDataStmts.push(`if (key.equals(${JSON.stringify(aliasName)})) {
103
121
  this.${name} = __atoi_fast<${type}>(data, val_start << 1, val_end << 1);
104
122
  return;
105
123
  }`);
106
- if (member.initializer) {
107
- this.currentClass.initializeStmts.push(`this.${name} = ${toString(member.initializer)}`);
108
- }
124
+ if (member.initializer) {
125
+ this.currentClass.initializeStmts.push(`this.${name} = ${toString(member.initializer)}`);
109
126
  }
110
- else // @ts-ignore
111
- if ([
112
- "f32",
113
- "f64",
114
- ].includes(type.toLowerCase())) {
115
- this.currentClass.encodeStmts.push(`${encodeKey(aliasName)}:\${this.${name}},`);
116
- // @ts-ignore
117
- this.currentClass.setDataStmts.push(`if (key.equals(${JSON.stringify(aliasName)})) {
127
+ }
128
+ else if ([
129
+ "f32",
130
+ "f64",
131
+ ].includes(type.toLowerCase())) {
132
+ if (omitif) {
133
+ this.currentClass.encodeStmts.push(`\${${omitif} ? "" : \`,${encodeKey(aliasName)}:\${this.${name}}\`}`);
134
+ }
135
+ else {
136
+ this.currentClass.encodeStmts.push(`,${encodeKey(aliasName)}:\${this.${name}}`);
137
+ }
138
+ // @ts-ignore
139
+ this.currentClass.setDataStmts.push(`if (key.equals(${JSON.stringify(aliasName)})) {
118
140
  this.${name} = __parseObjectValue<${type}>(data.slice(val_start, val_end), initializeDefaultValues);
119
141
  return;
120
142
  }`);
121
- if (member.initializer) {
122
- this.currentClass.initializeStmts.push(`this.${name} = ${toString(member.initializer)}`);
123
- }
143
+ if (member.initializer) {
144
+ this.currentClass.initializeStmts.push(`this.${name} = ${toString(member.initializer)}`);
145
+ }
146
+ }
147
+ else {
148
+ if (omitnull) {
149
+ this.currentClass.encodeStmts.push(`c\${changetype<usize>(this.${name}) !== <usize>0 ? "" : \`${encodeKey(aliasName)}:\${__JSON_Stringify<${type}>(this.${name})}\`}`);
150
+ console.log(`\${changetype<usize>(this.${name}) != <usize>0 ? \`${encodeKey(aliasName)}:,\${__JSON_Stringify<${type}>(this.${name})}\` : ""}`);
151
+ }
152
+ else if (omitif) {
153
+ this.currentClass.encodeStmts.push(`\${${omitif} ? "" : \`${encodeKey(aliasName)}:,\${__JSON_Stringify<${type}>(this.${name})}\`}`);
124
154
  }
125
155
  else {
126
- this.currentClass.encodeStmts.push(`${encodeKey(aliasName)}:\${__JSON_Stringify<${type}>(this.${name})},`);
127
- // @ts-ignore
128
- this.currentClass.setDataStmts.push(`if (key.equals(${JSON.stringify(aliasName)})) {
156
+ this.currentClass.encodeStmts.push(`,${encodeKey(aliasName)}:\${__JSON_Stringify<${type}>(this.${name})}`);
157
+ }
158
+ // @ts-ignore
159
+ this.currentClass.setDataStmts.push(`if (key.equals(${JSON.stringify(aliasName)})) {
129
160
  this.${name} = __parseObjectValue<${type}>(val_start ? data.slice(val_start, val_end) : data, initializeDefaultValues);
130
161
  return;
131
162
  }`);
132
- if (member.initializer) {
133
- this.currentClass.initializeStmts.push(`this.${name} = ${toString(member.initializer)}`);
134
- }
163
+ if (member.initializer) {
164
+ this.currentClass.initializeStmts.push(`this.${name} = ${toString(member.initializer)}`);
135
165
  }
136
166
  }
137
167
  }
138
168
  }
139
169
  let serializeFunc = "";
140
170
  if (this.currentClass.encodeStmts.length > 0) {
141
- const stmt = this.currentClass.encodeStmts[this.currentClass.encodeStmts.length - 1];
142
- this.currentClass.encodeStmts[this.currentClass.encodeStmts.length - 1] =
143
- stmt.slice(0, stmt.length - 1);
171
+ const stmt = this.currentClass.encodeStmts[0];
172
+ this.currentClass.encodeStmts[0] = stmt.slice(1);
144
173
  serializeFunc = `
145
174
  __JSON_Serialize(): string {
146
175
  return \`{${this.currentClass.encodeStmts.join("")}}\`;
@@ -179,7 +208,7 @@ class AsJSONTransform extends BaseVisitor {
179
208
  this.schemasList.push(this.currentClass);
180
209
  this.sources.add(node.name.range.source);
181
210
  // Uncomment to see the generated code for debugging.
182
- //console.log(serializeFunc);
211
+ console.log(serializeFunc);
183
212
  //console.log(setKeyFunc);
184
213
  //console.log(initializeFunc);
185
214
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@json-as/transform",
3
- "version": "0.8.3",
3
+ "version": "0.8.5",
4
4
  "description": "JSON encoder/decoder for AssemblyScript",
5
5
  "main": "./lib/index.js",
6
6
  "author": "Jairus Tanaka",
@@ -25,6 +25,8 @@
25
25
  "json",
26
26
  "serialize",
27
27
  "deserialize",
28
+ "stringify",
29
+ "data",
28
30
  "serde"
29
31
  ],
30
32
  "bugs": {
@@ -61,24 +61,33 @@ class AsJSONTransform extends BaseVisitor {
61
61
  initializeStmts: []
62
62
  };
63
63
 
64
- if (this.currentClass.parent.length > 0) {
64
+ if (this.currentClass.parent.length) {
65
65
  const parentSchema = this.schemasList.find(
66
66
  (v) => v.name == this.currentClass.parent
67
67
  );
68
68
  if (parentSchema?.encodeStmts) {
69
69
  parentSchema?.encodeStmts.push(parentSchema?.encodeStmts.pop() + ",");
70
- this.currentClass.encodeStmts.push(...parentSchema?.encodeStmts);
70
+ for (let i = 0; i < parentSchema.keys.length; i++) {
71
+ const key = parentSchema.keys[i];
72
+ if (node.members.filter(v => v.name.text == key) == undefined) this.currentClass.encodeStmts.unshift(parentSchema.encodeStmts[i]!);
73
+ }
71
74
  }
72
75
  }
73
76
 
74
77
  const parentSchema = this.schemasList.find(
75
78
  (v) => v.name == this.currentClass.parent
76
79
  );
80
+
77
81
  const members = [
78
- ...node.members,
79
- ...(parentSchema ? parentSchema.node.members : []),
82
+ ...node.members
80
83
  ];
81
84
 
85
+ if (parentSchema) {
86
+ for (const mem of parentSchema.node.members) {
87
+ if (members.find(v => v.name === mem.name) == undefined) members.unshift(mem);
88
+ }
89
+ }
90
+
82
91
  for (const mem of members) {
83
92
  // @ts-ignore
84
93
  if (mem.type && mem.type.name && mem.type.name.identifier.text) {
@@ -86,91 +95,116 @@ class AsJSONTransform extends BaseVisitor {
86
95
  const lineText = toString(member);
87
96
  //console.log("Member: " + lineText)
88
97
 
89
- if (!lineText.startsWith("private ") && !lineText.startsWith("static ")) {
98
+ if (lineText.startsWith("private ") && lineText.startsWith("static ")) continue;
90
99
 
91
- // @ts-ignore
92
- let type = toString(member.type);
100
+ const omitnull = member.decorators?.find(v => v.name.text == "omitnull");
101
+ console.log(member.decorators?.find(v => v.name.text === "omitif"))
102
+ const omitif = member.decorators?.find(v => v.name.text === "omitif")?.args?.[0]?.value ?? null;
93
103
 
94
- const name = member.name.text;
95
- let aliasName = name;
104
+ // @ts-ignore
105
+ let type = toString(member.type);
96
106
 
97
- // @ts-ignore
98
- if (member.decorators && member.decorators[0]?.name.text === "alias") {
99
- if (member.decorators[0] && member.decorators[0].args![0]) {
100
- // @ts-ignore
101
- aliasName = member.decorators[0].args![0].value;
102
- }
107
+ const name = member.name.text;
108
+ let aliasName = name;
109
+
110
+ // @ts-ignore
111
+ if (member.decorators && member.decorators[0]?.name.text === "alias") {
112
+ if (member.decorators[0] && member.decorators[0].args![0]) {
113
+ // @ts-ignore
114
+ aliasName = member.decorators[0].args![0].value;
103
115
  }
104
- this.currentClass.keys.push(name);
105
- // @ts-ignore
106
- this.currentClass.types.push(type);
107
- // @ts-ignore
108
- if (
109
- [
110
- "u8",
111
- "i8",
112
- "u16",
113
- "i16",
114
- "u32",
115
- "i32",
116
- "u64",
117
- "i64",
118
- ].includes(type.toLowerCase())
119
- ) {
116
+ }
117
+ this.currentClass.keys.push(name);
118
+ // @ts-ignore
119
+ this.currentClass.types.push(type);
120
+ // @ts-ignore
121
+ if (
122
+ [
123
+ "u8",
124
+ "i8",
125
+ "u16",
126
+ "i16",
127
+ "u32",
128
+ "i32",
129
+ "u64",
130
+ "i64",
131
+ ].includes(type.toLowerCase())
132
+ ) {
133
+ if (omitif) {
120
134
  this.currentClass.encodeStmts.push(
121
- `${encodeKey(aliasName)}:\${this.${name}},`
135
+ `\${${omitif} ? "" : \`,${encodeKey(aliasName)}:\${this.${name}}\`}`
122
136
  );
123
- // @ts-ignore
124
- this.currentClass.setDataStmts.push(
125
- `if (key.equals(${JSON.stringify(aliasName)})) {
137
+ } else {
138
+ this.currentClass.encodeStmts.push(
139
+ `,${encodeKey(aliasName)}:\${this.${name}}`
140
+ );
141
+ }
142
+ // @ts-ignore
143
+ this.currentClass.setDataStmts.push(
144
+ `if (key.equals(${JSON.stringify(aliasName)})) {
126
145
  this.${name} = __atoi_fast<${type}>(data, val_start << 1, val_end << 1);
127
146
  return;
128
147
  }`
148
+ );
149
+ if (member.initializer) {
150
+ this.currentClass.initializeStmts.push(
151
+ `this.${name} = ${toString(member.initializer)}`
129
152
  );
130
- if (member.initializer) {
131
- this.currentClass.initializeStmts.push(
132
- `this.${name} = ${toString(member.initializer)}`
133
- );
134
- }
135
- } else // @ts-ignore
136
- if (
137
- [
138
- "f32",
139
- "f64",
140
- ].includes(type.toLowerCase())
141
- ) {
142
- this.currentClass.encodeStmts.push(
143
- `${encodeKey(aliasName)}:\${this.${name}},`
144
- );
145
- // @ts-ignore
146
- this.currentClass.setDataStmts.push(
147
- `if (key.equals(${JSON.stringify(aliasName)})) {
153
+ }
154
+ } else if (
155
+ [
156
+ "f32",
157
+ "f64",
158
+ ].includes(type.toLowerCase())
159
+ ) {
160
+ if (omitif) {
161
+ this.currentClass.encodeStmts.push(
162
+ `\${${omitif} ? "" : \`,${encodeKey(aliasName)}:\${this.${name}}\`}`
163
+ );
164
+ } else {
165
+ this.currentClass.encodeStmts.push(
166
+ `,${encodeKey(aliasName)}:\${this.${name}}`
167
+ );
168
+ }
169
+ // @ts-ignore
170
+ this.currentClass.setDataStmts.push(
171
+ `if (key.equals(${JSON.stringify(aliasName)})) {
148
172
  this.${name} = __parseObjectValue<${type}>(data.slice(val_start, val_end), initializeDefaultValues);
149
173
  return;
150
174
  }`
151
- );
152
- if (member.initializer) {
153
- this.currentClass.initializeStmts.push(
154
- `this.${name} = ${toString(member.initializer)}`
155
- );
156
- }
157
- } else {
158
- this.currentClass.encodeStmts.push(
159
- `${encodeKey(aliasName)}:\${__JSON_Stringify<${type}>(this.${name})},`
160
- );
161
- // @ts-ignore
162
- this.currentClass.setDataStmts.push(
163
- `if (key.equals(${JSON.stringify(aliasName)})) {
175
+ );
176
+ if (member.initializer) {
177
+ this.currentClass.initializeStmts.push(
178
+ `this.${name} = ${toString(member.initializer)}`
179
+ );
180
+ }
181
+ } else {
182
+ if (omitnull) {
183
+ this.currentClass.encodeStmts.push(
184
+ `${comma}\${changetype<usize>(this.${name}) !== <usize>0 ? "" : \`${encodeKey(aliasName)}:\${__JSON_Stringify<${type}>(this.${name})}\`}`
185
+ );
186
+ console.log(`\${changetype<usize>(this.${name}) != <usize>0 ? \`${encodeKey(aliasName)}:,\${__JSON_Stringify<${type}>(this.${name})}\` : ""}`)
187
+ } else if (omitif) {
188
+ this.currentClass.encodeStmts.push(
189
+ `\${${omitif} ? "" : \`${encodeKey(aliasName)}:,\${__JSON_Stringify<${type}>(this.${name})}\`}`
190
+ );
191
+ } else {
192
+ this.currentClass.encodeStmts.push(
193
+ `,${encodeKey(aliasName)}:\${__JSON_Stringify<${type}>(this.${name})}`
194
+ );
195
+ }
196
+ // @ts-ignore
197
+ this.currentClass.setDataStmts.push(
198
+ `if (key.equals(${JSON.stringify(aliasName)})) {
164
199
  this.${name} = __parseObjectValue<${type}>(val_start ? data.slice(val_start, val_end) : data, initializeDefaultValues);
165
200
  return;
166
201
  }`
167
- );
168
- if (member.initializer) {
169
- this.currentClass.initializeStmts.push(
170
- `this.${name} = ${toString(member.initializer)}`
171
- );
172
- }
173
- }
202
+ );
203
+ if (member.initializer) {
204
+ this.currentClass.initializeStmts.push(
205
+ `this.${name} = ${toString(member.initializer)}`
206
+ );
207
+ }
174
208
  }
175
209
  }
176
210
  }
@@ -179,11 +213,8 @@ class AsJSONTransform extends BaseVisitor {
179
213
 
180
214
  if (this.currentClass.encodeStmts.length > 0) {
181
215
  const stmt =
182
- this.currentClass.encodeStmts[
183
- this.currentClass.encodeStmts.length - 1
184
- ]!;
185
- this.currentClass.encodeStmts[this.currentClass.encodeStmts.length - 1] =
186
- stmt!.slice(0, stmt.length - 1);
216
+ this.currentClass.encodeStmts[0]!;
217
+ this.currentClass.encodeStmts[0] = stmt!.slice(1);
187
218
  serializeFunc = `
188
219
  __JSON_Serialize(): string {
189
220
  return \`{${this.currentClass.encodeStmts.join("")}}\`;
@@ -227,7 +258,7 @@ class AsJSONTransform extends BaseVisitor {
227
258
  this.sources.add(node.name.range.source);
228
259
 
229
260
  // Uncomment to see the generated code for debugging.
230
- //console.log(serializeFunc);
261
+ console.log(serializeFunc);
231
262
  //console.log(setKeyFunc);
232
263
  //console.log(initializeFunc);
233
264
  }