## Example
```typescript before
/** @derive(Deserialize) @serde({ denyUnknownFields: true }) */
class User {
id: number;
/** @serde({ validate: { email: true, maxLength: 255 } }) */
email: string;
/** @serde({ default: "guest" }) */
name: string;
/** @serde({ validate: { positive: true } }) */
age?: number;
}
```
```typescript after
import { DeserializeContext } from 'macroforge/serde';
import { DeserializeError } from 'macroforge/serde';
import type { DeserializeOptions } from 'macroforge/serde';
import { PendingRef } from 'macroforge/serde';
/** @serde({ denyUnknownFields: true }) */
class User {
id: number;
email: string;
name: string;
age?: number;
constructor(props: {
id: number;
email: string;
name?: string;
age?: number;
}) {
this.id = props.id;
this.email = props.email;
this.name = props.name as string;
this.age = props.age as number;
}
/**
* Deserializes input to an instance of this class.
* Automatically detects whether input is a JSON string or object.
* @param input - JSON string or object to deserialize
* @param opts - Optional deserialization options
* @returns Result containing the deserialized instance or validation errors
*/
static deserialize(
input: unknown,
opts?: @{DESERIALIZE_OPTIONS}
): Result<
User,
Array<{
field: string;
message: string;
}>
> {
try {
// Auto-detect: if string, parse as JSON first
const data = typeof input === 'string' ? JSON.parse(input) : input;
const ctx = @{DESERIALIZE_CONTEXT}.create();
const resultOrRef = User.deserializeWithContext(data, ctx);
if (@{PENDING_REF}.is(resultOrRef)) {
return Result.err([
{
field: '_root',
message: 'User.deserialize: root cannot be a forward reference'
}
]);
}
ctx.applyPatches();
if (opts?.freeze) {
ctx.freezeAll();
}
return Result.ok(resultOrRef);
} catch (e) {
if (e instanceof @{DESERIALIZE_ERROR}) {
return Result.err(e.errors);
}
const message = e instanceof Error ? e.message : String(e);
return Result.err([
{
field: '_root',
message
}
]);
}
}
/** @internal */
static deserializeWithContext(value: any, ctx: @{DESERIALIZE_CONTEXT}): User | @{PENDING_REF} {
if (value?.__ref !== undefined) {
return ctx.getOrDefer(value.__ref);
}
if (typeof value !== 'object' || value === null || Array.isArray(value)) {
throw new @{DESERIALIZE_ERROR}([
{
field: '_root',
message: 'User.deserializeWithContext: expected an object'
}
]);
}
const obj = value as Record<string, unknown>;
const errors: Array<{
field: string;
message: string;
}> = [];
const knownKeys = new Set(['__type', '__id', '__ref', 'id', 'email', 'name', 'age']);
for (const key of Object.keys(obj)) {
if (!knownKeys.has(key)) {
errors.push({
field: key,
message: 'unknown field'
});
}
}
if (!('id' in obj)) {
errors.push({
field: 'id',
message: 'missing required field'
});
}
if (!('email' in obj)) {
errors.push({
field: 'email',
message: 'missing required field'
});
}
if (errors.length > 0) {
throw new @{DESERIALIZE_ERROR}(errors);
}
const instance = Object.create(User.prototype) as User;
if (obj.__id !== undefined) {
ctx.register(obj.__id as number, instance);
}
ctx.trackForFreeze(instance);
{
const __raw_id = obj['id'] as number;
instance.id = __raw_id;
}
{
const __raw_email = obj['email'] as string;
instance.email = __raw_email;
}
if ('name' in obj && obj['name'] !== undefined) {
const __raw_name = obj['name'] as string;
instance.name = __raw_name;
} else {
instance.name = "guest";
}
if ('age' in obj && obj['age'] !== undefined) {
const __raw_age = obj['age'] as number;
instance.age = __raw_age;
}
if (errors.length > 0) {
throw new @{DESERIALIZE_ERROR}(errors);
}
return instance;
}
static validateField<K extends keyof User>(
field: K,
value: User[K]
): Array<{
field: string;
message: string;
}> {
return [];
}
static validateFields(partial: Partial<User>): Array<{
field: string;
message: string;
}> {
return [];
}
static hasShape(obj: unknown): boolean {
if (typeof obj !== 'object' || obj === null || Array.isArray(obj)) {
return false;
}
const o = obj as Record<string, unknown>;
return 'id' in o && 'email' in o;
}
static is(obj: unknown): obj is User {
if (obj instanceof User) {
return true;
}
if (!User.hasShape(obj)) {
return false;
}
const result = User.deserialize(obj);
return Result.isOk(result);
}
}
// Usage:
const result = User.deserialize('{"id":1,"email":"test@example.com"}');
if (Result.isOk(result)) {
const user = result.value;
} else {
console.error(result.error); // [{ field: "email", message: "must be a valid email" }]
}
```
## Required Imports
The generated code automatically imports:
- `DeserializeContext`, `DeserializeError`, `PendingRef` from `macroforge/serde`