json-as 1.0.8 → 1.1.0

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.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # Change Log
2
2
 
3
+ ## 2025-05-22 - 1.1.0
4
+
5
+ - fix: change __DESERIALIZE<T> to __JSON_T to avoid populating local scope
6
+
7
+ ## 2025-05-22 - 1.0.9
8
+
9
+ - fix: [#132](https://github.com/JairusSW/json-as/issues/132)
10
+ - feat: allow base classes to use their child classes if the signatures match
11
+ - perf: rewrite struct deserialization to be significantly faster
12
+ - fix: [#131](https://github.com/JairusSW/json-as/issues/131) Generic classes with custom deserializer crashing
13
+ - fix: [#66](https://github.com/JairusSW/json-as/issues/66) Throw error when additional keys are in JSON
14
+
3
15
  ## 2025-05-21 - 1.0.8
4
16
 
5
17
  - fix: inline warnings on layer-2 serialize and deserialize functions
package/README.md CHANGED
@@ -6,7 +6,7 @@
6
6
  ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
7
7
  █████ ███████ ██████ ██ ████ ██ ██ ███████
8
8
  </span>
9
- AssemblyScript - v1.0.8
9
+ AssemblyScript - v1.1.0
10
10
  </pre>
11
11
  </h6>
12
12
 
@@ -57,6 +57,7 @@ If you'd like to see the code that the transform generates, run the build step w
57
57
  ```typescript
58
58
  import { JSON } from "json-as";
59
59
 
60
+
60
61
  @json
61
62
  class Vec3 {
62
63
  x: f32 = 0.0;
@@ -64,6 +65,7 @@ class Vec3 {
64
65
  z: f32 = 0.0;
65
66
  }
66
67
 
68
+
67
69
  @json
68
70
  class Player {
69
71
  @alias("first name")
@@ -109,6 +111,7 @@ This library allows selective omission of fields during serialization using the
109
111
  This decorator excludes a field from serialization entirely.
110
112
 
111
113
  ```typescript
114
+
112
115
  @json
113
116
  class Example {
114
117
  name!: string;
@@ -128,6 +131,7 @@ console.log(JSON.stringify(obj)); // { "name": "Jairus" }
128
131
  This decorator omits a field only if its value is null.
129
132
 
130
133
  ```typescript
134
+
131
135
  @json
132
136
  class Example {
133
137
  name!: string;
@@ -147,6 +151,7 @@ console.log(JSON.stringify(obj)); // { "name": "Jairus" }
147
151
  This decorator omits a field based on a custom predicate function.
148
152
 
149
153
  ```typescript
154
+
150
155
  @json
151
156
  class Example {
152
157
  name!: string;
@@ -174,6 +179,7 @@ AssemblyScript doesn't support using nullable primitive types, so instead, json-
174
179
  For example, this schema won't compile in AssemblyScript:
175
180
 
176
181
  ```typescript
182
+
177
183
  @json
178
184
  class Person {
179
185
  name!: string;
@@ -184,6 +190,7 @@ class Person {
184
190
  Instead, use `JSON.Box` to allow nullable primitives:
185
191
 
186
192
  ```typescript
193
+
187
194
  @json
188
195
  class Person {
189
196
  name: string;
@@ -246,6 +253,7 @@ More often, objects will be completely statically typed except for one or two va
246
253
  In such cases, `JSON.Value` can be used to handle fields that may hold different types at runtime.
247
254
 
248
255
  ```typescript
256
+
249
257
  @json
250
258
  class DynamicObj {
251
259
  id: i32 = 0;
@@ -302,6 +310,7 @@ Here's an example of creating a custom data type called `Point` which serializes
302
310
  ```typescript
303
311
  import { bytes } from "json-as/assembly/util";
304
312
 
313
+
305
314
  @json
306
315
  class Point {
307
316
  x: f64 = 0.0;
@@ -1,6 +1,6 @@
1
1
  export function bench(description: string, routine: () => void, ops: u64 = 1_000_000): void {
2
2
  console.log(" - Benchmarking " + description);
3
- let warmup = ops/10;
3
+ let warmup = ops / 10;
4
4
  while (--warmup) {
5
5
  routine();
6
6
  }
@@ -7,52 +7,6 @@ class Vec3 {
7
7
  public x!: i32;
8
8
  public y!: i32;
9
9
  public z!: i32;
10
-
11
-
12
- @inline __SERIALIZE(ptr: usize): void {
13
- bs.proposeSize(98);
14
- store<u64>(bs.offset, 9570664606466171, 0); // {"x"
15
- store<u16>(bs.offset, 58, 8); // :
16
- bs.offset += 10;
17
- JSON.__serialize<i32>(load<i32>(ptr, offsetof<this>("x")));
18
- store<u64>(bs.offset, 9570668901433388, 0); // ,"y"
19
- store<u16>(bs.offset, 58, 8); // :
20
- bs.offset += 10;
21
- JSON.__serialize<i32>(load<i32>(ptr, offsetof<this>("y")));
22
- store<u64>(bs.offset, 9570673196400684, 0); // ,"z"
23
- store<u16>(bs.offset, 58, 8); // :
24
- bs.offset += 10;
25
- JSON.__serialize<i32>(load<i32>(ptr, offsetof<this>("z")));
26
- store<u16>(bs.offset, 125, 0); // }
27
- bs.offset += 2;
28
- }
29
-
30
-
31
- @inline __INITIALIZE(): this {
32
- return this;
33
- }
34
-
35
-
36
- @inline __DESERIALIZE(keyStart: usize, keyEnd: usize, valStart: usize, valEnd: usize, ptr: usize): void {
37
- switch (load<u16>(keyStart)) {
38
- case 120: {
39
- // x
40
- store<i32>(ptr, JSON.__deserialize<i32>(valStart, valEnd, 0), offsetof<this>("x"));
41
- return;
42
- }
43
- case 121: {
44
- // y
45
- store<i32>(ptr, JSON.__deserialize<i32>(valStart, valEnd, 0), offsetof<this>("y"));
46
- return;
47
- }
48
- case 122: {
49
- // z
50
- store<i32>(ptr, JSON.__deserialize<i32>(valStart, valEnd, 0), offsetof<this>("z"));
51
- return;
52
- }
53
- }
54
- return;
55
- }
56
10
  }
57
11
 
58
12
  const v1: Vec3 = { x: 1, y: 2, z: 3 };
@@ -32,11 +32,12 @@ class Point {
32
32
  }
33
33
  }
34
34
 
35
+
35
36
  @json
36
- export class ObjectWithCustom {
37
- value: Point = new Point(0, 0)
37
+ class ObjectWithCustom {
38
+ value: Point = new Point(0, 0);
38
39
  constructor(value: Point) {
39
- this.value = value
40
+ this.value = value;
40
41
  }
41
42
  }
42
43
 
@@ -0,0 +1,55 @@
1
+ import { OBJECT, TOTAL_OVERHEAD } from "rt/common";
2
+ import { JSON } from "..";
3
+ import { describe, expect } from "./lib";
4
+
5
+ @json
6
+ class Foo {
7
+ a: i32 = 0;
8
+ }
9
+
10
+ @json
11
+ class Bar extends Foo {
12
+ b: i32 = 0;
13
+
14
+ @serializer
15
+ serialize(self: Bar): string {
16
+ return `"bar"`;
17
+ }
18
+
19
+ @deserializer
20
+ deserialize(data: string): Bar {
21
+ return data == "\"bar\"" ? {
22
+ a: 1,
23
+ b: 2,
24
+ } : new Bar();
25
+ }
26
+ }
27
+
28
+ describe("should use custom serializer for subclasses", () => {
29
+ const bar = new Bar();
30
+ bar.a = 1;
31
+ bar.b = 2;
32
+ const data = JSON.stringify(bar);
33
+ expect(data).toBe('"bar"');
34
+ });
35
+
36
+ describe("should use custom serializer for subclasses when type is the parent", () => {
37
+ const bar = new Bar();
38
+ bar.a = 1;
39
+ bar.b = 2;
40
+ const data = JSON.stringify<Foo>(bar);
41
+ expect(data).toBe('"bar"');
42
+ });
43
+
44
+ describe("should use custom deserializer for subclass", () => {
45
+ const json = '"bar"';
46
+ const bar = JSON.parse<Bar>(json);
47
+ expect(bar.a.toString()).toBe("1");
48
+ expect(bar.b.toString()).toBe("2");
49
+ });
50
+
51
+ describe("should use custom deserializer even when type is the parent", () => {
52
+ const json = '"bar"';
53
+ const foo = JSON.parse<Bar>(json);
54
+ expect(foo.a.toString()).toBe("1");
55
+ });
@@ -1,5 +1,6 @@
1
1
  export function describe(description: string, routine: () => void): void {
2
2
  routine();
3
+ // console.log(" " + description + " OK");
3
4
  }
4
5
 
5
6
  export function expect(left: string): Expectation {
@@ -3,43 +3,43 @@ import { COMMA, BRACKET_RIGHT } from "../../../custom/chars";
3
3
  import { JSON } from "../../..";
4
4
 
5
5
  export function deserializeBoxArray<T extends JSON.Box<any>[]>(srcStart: usize, srcEnd: usize, dst: usize): T {
6
- const out = changetype<nonnull<T>>(dst || changetype<usize>(instantiate<T>()));
7
- if (isBoolean<valueof<T>>()) {
8
- srcStart += 2; // skip [
9
- while (srcStart < srcEnd) {
10
- const block = load<u64>(srcStart);
11
- if (block == 28429475166421108) {
12
- out.push(JSON.Box.from(true));
13
- srcStart += 10;
14
- } else if (block == 32370086184550502 && load<u16>(srcStart, 8) == 101) {
15
- out.push(JSON.Box.from(false));
16
- srcStart += 12;
17
- } else {
18
- srcStart += 2;
19
- }
20
- }
21
- return out;
22
- } else {
23
- let lastIndex: usize = 0;
6
+ const out = changetype<nonnull<T>>(dst || changetype<usize>(instantiate<T>()));
7
+ if (isBoolean<valueof<T>>()) {
8
+ srcStart += 2; // skip [
9
+ while (srcStart < srcEnd) {
10
+ const block = load<u64>(srcStart);
11
+ if (block == 28429475166421108) {
12
+ out.push(JSON.Box.from(true));
13
+ srcStart += 10;
14
+ } else if (block == 32370086184550502 && load<u16>(srcStart, 8) == 101) {
15
+ out.push(JSON.Box.from(false));
16
+ srcStart += 12;
17
+ } else {
18
+ srcStart += 2;
19
+ }
20
+ }
21
+ return out;
22
+ } else {
23
+ let lastIndex: usize = 0;
24
+ while (srcStart < srcEnd) {
25
+ const code = load<u16>(srcStart);
26
+ if (code - 48 <= 9 || code == 45) {
27
+ lastIndex = srcStart;
28
+ srcStart += 2;
24
29
  while (srcStart < srcEnd) {
25
- const code = load<u16>(srcStart);
26
- if (code - 48 <= 9 || code == 45) {
27
- lastIndex = srcStart;
28
- srcStart += 2;
29
- while (srcStart < srcEnd) {
30
- const code = load<u16>(srcStart);
31
- if (code == COMMA || code == BRACKET_RIGHT || isSpace(code)) {
32
- out.push(JSON.__deserialize<valueof<T>>(lastIndex, srcStart));
33
- // while (isSpace(load<u16>((srcStart += 2)))) {
34
- // /* empty */
35
- // }
36
- break;
37
- }
38
- srcStart += 2;
39
- }
40
- }
41
- srcStart += 2;
30
+ const code = load<u16>(srcStart);
31
+ if (code == COMMA || code == BRACKET_RIGHT || isSpace(code)) {
32
+ out.push(JSON.__deserialize<valueof<T>>(lastIndex, srcStart));
33
+ // while (isSpace(load<u16>((srcStart += 2)))) {
34
+ // /* empty */
35
+ // }
36
+ break;
37
+ }
38
+ srcStart += 2;
42
39
  }
40
+ }
41
+ srcStart += 2;
43
42
  }
44
- return out;
43
+ }
44
+ return out;
45
45
  }
@@ -37,7 +37,7 @@ export function deserializeArray<T extends unknown[]>(srcStart: usize, srcEnd: u
37
37
  } else if (type instanceof JSON.Obj) {
38
38
  // @ts-ignore: type
39
39
  return deserializeObjectArray<T>(srcStart, srcEnd, dst);
40
- }else if (type instanceof Map) {
40
+ } else if (type instanceof Map) {
41
41
  // @ts-ignore: type
42
42
  return deserializeMapArray<T>(srcStart, srcEnd, dst);
43
43
  // @ts-ignore: defined by transform
package/assembly/index.ts CHANGED
@@ -28,9 +28,6 @@ import { serializeObject } from "./serialize/simple/object";
28
28
  import { deserializeObject } from "./deserialize/simple/object";
29
29
  import { serializeRaw } from "./serialize/simple/raw";
30
30
  import { deserializeRaw } from "./deserialize/simple/raw";
31
- import { isSpace } from "util/string";
32
- import { deserializeString_SIMD } from "./deserialize/simd/string";
33
- import { serializeString_SIMD } from "./serialize/simd/string";
34
31
 
35
32
  /**
36
33
  * Offset of the 'storage' property in the JSON.Value class.
@@ -119,15 +116,10 @@ export namespace JSON {
119
116
  // if (ASC_FEATURE_SIMD) {
120
117
  // serializeString_SIMD(data as string);
121
118
  // } else {
122
- serializeString(data as string);
119
+ serializeString(data as string);
123
120
  // }
124
121
  return bs.out<string>();
125
122
  // @ts-ignore: Supplied by transform
126
- } else if (isDefined(data.__SERIALIZE_CUSTOM)) {
127
- // @ts-ignore
128
- inline.always(data.__SERIALIZE_CUSTOM());
129
- return bs.out<string>();
130
- // @ts-ignore: Supplied by transform
131
123
  } else if (isDefined(data.__SERIALIZE)) {
132
124
  // @ts-ignore
133
125
  inline.always(data.__SERIALIZE(changetype<usize>(data)));
@@ -200,19 +192,12 @@ export namespace JSON {
200
192
  } else {
201
193
  let type: nonnull<T> = changetype<nonnull<T>>(0);
202
194
  // @ts-ignore: Defined by transform
203
- if (isDefined(type.__DESERIALIZE_CUSTOM)) {
204
- const out = changetype<nonnull<T>>(0);
195
+ if (isDefined(type.__DESERIALIZE)) {
196
+ const out = changetype<nonnull<T>>(__new(offsetof<nonnull<T>>(), idof<nonnull<T>>()));
205
197
  // @ts-ignore: Defined by transform
206
198
  if (isDefined(type.__INITIALIZE)) out.__INITIALIZE();
207
199
  // @ts-ignore
208
- return out.__DESERIALIZE_CUSTOM(ptrToStr(dataPtr, dataPtr + dataSize));
209
- // @ts-ignore: Defined by transform
210
- } else if (isDefined(type.__DESERIALIZE)) {
211
- const out = __new(offsetof<nonnull<T>>(), idof<nonnull<T>>());
212
- // @ts-ignore: Defined by transform
213
- if (isDefined(type.__INITIALIZE)) changetype<nonnull<T>>(out).__INITIALIZE();
214
- // @ts-ignore
215
- return inline.always(deserializeStruct<nonnull<T>>(dataPtr, dataPtr + dataSize, out));
200
+ return out.__DESERIALIZE(dataPtr, dataPtr + dataSize, out);
216
201
  } else if (type instanceof Map) {
217
202
  // @ts-ignore
218
203
  return inline.always(deserializeMap<nonnull<T>>(dataPtr, dataPtr + dataSize, 0));
@@ -408,7 +393,7 @@ export namespace JSON {
408
393
  case JSON.Types.Array: {
409
394
  const arr = this.get<JSON.Value[]>();
410
395
  if (!arr.length) return "[]";
411
- let out = "["
396
+ let out = "[";
412
397
  const end = arr.length - 1;
413
398
  for (let i = 0; i < end; i++) {
414
399
  const element = unchecked(arr[i]);
@@ -440,7 +425,7 @@ export namespace JSON {
440
425
  // @ts-ignore: type
441
426
  private storage: Map<string, JSON.Value> = new Map<string, JSON.Value>();
442
427
 
443
- constructor() { }
428
+ constructor() {}
444
429
 
445
430
  // @ts-ignore: decorator
446
431
  @inline get size(): i32 {
@@ -602,15 +587,10 @@ export namespace JSON {
602
587
  } else {
603
588
  let type: nonnull<T> = changetype<nonnull<T>>(0);
604
589
  // @ts-ignore: Defined by transform
605
- if (isDefined(type.__DESERIALIZE_CUSTOM)) {
606
- const out = __new(offsetof<nonnull<T>>(), idof<nonnull<T>>());
607
- // @ts-ignore: Defined by transform
608
- if (isDefined(type.__INITIALIZE)) changetype<nonnull<T>>(out).__INITIALIZE();
609
- // @ts-ignore
610
- return changetype<nonnull<T>>(out).__DESERIALIZE_CUSTOM(ptrToStr(srcStart, srcEnd));
590
+ if (isDefined(type.__DESERIALIZE)) {
591
+ const out = changetype<nonnull<T>>(dst || __new(offsetof<nonnull<T>>(), idof<nonnull<T>>()))
611
592
  // @ts-ignore: Defined by transform
612
- } else if (isDefined(type.__DESERIALIZE)) {
613
- return deserializeStruct<T>(srcStart, srcEnd, dst);
593
+ return out.__DESERIALIZE(srcStart, srcEnd, out);
614
594
  } else if (type instanceof Map) {
615
595
  // @ts-ignore: type
616
596
  return deserializeMap<T>(srcStart, srcEnd, dst);
@@ -623,7 +603,7 @@ export namespace JSON {
623
603
  } else if (type instanceof JSON.Value) {
624
604
  // @ts-ignore: type
625
605
  return deserializeArbitrary(srcStart, srcEnd, 0);
626
- }else if (type instanceof JSON.Obj) {
606
+ } else if (type instanceof JSON.Obj) {
627
607
  // @ts-ignore: type
628
608
  return deserializeObject(srcStart, srcEnd, 0);
629
609
  } else if (type instanceof JSON.Box) {
@@ -633,6 +613,19 @@ export namespace JSON {
633
613
  }
634
614
  throw new Error(`Could not deserialize data '${ptrToStr(srcStart, srcEnd).slice(0, 100)}' to type. Make sure to add the correct decorators to classes.`);
635
615
  }
616
+ namespace Util {
617
+ // @ts-ignore: decorator
618
+ @inline export function isSpace(code: u16): boolean {
619
+ return code == 0x20 || code - 9 <= 4;
620
+ }
621
+ // @ts-ignore: decorator
622
+ @inline export function ptrToStr(start: usize, end: usize): string {
623
+ const size = end - start;
624
+ const out = __new(size, idof<string>());
625
+ memory.copy(out, start, size);
626
+ return changetype<string>(out);
627
+ }
628
+ }
636
629
  }
637
630
 
638
631
  // @ts-ignore: decorator