Basic Usage
Decorated classes
Any class marked @json can be parsed and serialized. Fields are matched to JSON keys by name.
import { JSON } from "json-as";
@json
class Point {
x: i32 = 0;
y: i32 = 0;
}
JSON.stringify(new Point()); // '{"x":0,"y":0}'
const p = JSON.parse<Point>('{"x":3,"y":4}');
p.y; // 4Give fields a default (x: i32 = 0) or mark them definitely-assigned (x!: i32). A default is used when the key is missing from the input.
Nested classes and arrays
Fields can be other @json classes, arrays of them, and arrays of primitives — nesting works to any depth.
@json
class Path {
name: string = "";
points: Point[] = [];
}
const path = JSON.parse<Path>(
'{"name":"a","points":[{"x":1,"y":2},{"x":3,"y":4}]}',
);
path.points[1].x; // 3
JSON.stringify(path); // '{"name":"a","points":[{"x":1,"y":2},{"x":3,"y":4}]}'Renaming keys with @alias
When the JSON key can't be an AssemblyScript identifier (or just differs from your field name), map it with @alias:
@json
class User {
@alias("user_id") id: i32 = 0;
name: string = "";
}
const u = new User();
u.id = 42;
u.name = "ada";
JSON.stringify(u); // '{"user_id":42,"name":"ada"}'
JSON.parse<User>('{"user_id":7,"name":"x"}').id; // 7Optional fields
Three decorators drop a field from the output (parsing still accepts the key either way):
@omit— never serialized.@omitnull()— skipped when the value isnull.@omitif(predicate)— skipped when the predicate (given the instance) returnstrue.
@json
class Session {
token: string = "";
@omit secret: string = "";
}
@json
class Profile {
name: string = "";
@omitnull() bio: string | null = null;
}
@json
class Player {
name: string = "";
@omitif((self: Player) => self.score == 0) score: i32 = 0;
}const s = new Session();
s.token = "t";
s.secret = "hidden";
JSON.stringify(s); // '{"token":"t"}' — secret never emitted
const pr = new Profile();
pr.name = "ada";
JSON.stringify(pr); // '{"name":"ada"}' — bio is null, skipped
pr.bio = "hi";
JSON.stringify(pr); // '{"bio":"hi","name":"ada"}'
const pl = new Player();
pl.name = "z";
JSON.stringify(pl); // '{"name":"z"}' — score is 0, skipped
pl.score = 9;
JSON.stringify(pl); // '{"score":9,"name":"z"}'Key order
Conditionally-emitted fields (@omitnull / @omitif) are serialized before the always-present ones — note bio and score appear first above. The order is still valid JSON; object key order isn't significant.
@omitif also accepts a string expression evaluated against the instance, e.g. @omitif("this.score == 0").
Nullable primitives with JSON.Box
AssemblyScript can't express i32 | null (value types aren't nullable). Wrap the primitive in JSON.Box<T> to get a nullable number/bool:
@json
class Account {
name: string = "";
balance: JSON.Box<i32> | null = null;
}
const a = JSON.parse<Account>('{"name":"a","balance":50}');
a.balance!.value; // 50
const b = JSON.parse<Account>('{"name":"a","balance":null}');
b.balance == null; // trueA Box<T> serializes as the bare value (or null), not as an object.
Reusing an instance
Both parse and stringify take an optional second argument to reuse an existing object or output string instead of allocating a fresh one — handy in hot loops over a fixed shape.
const into = new Point();
JSON.parse<Point>('{"x":1,"y":2}', into); // fills `into` in place
into.x; // 1
let out = "";
out = JSON.stringify(new Point(), out); // reuses `out`'s buffer when it canOn a steady-state re-parse of the same shape this reuses nested structs, arrays, and strings in place — effectively zero allocation after the first call.
Next steps
- Built-in Types —
Date, maps, sets, typed arrays, and more - Lazy Fields — defer parsing of fields you rarely touch
- Dynamic Types —
JSON.Value/JSON.Objfor unknown shapes
