# Serialize
The `Serialize` macro generates JSON serialization methods with **cycle detection**
and object identity tracking. This enables serialization of complex object graphs
including circular references.
## Generated Methods
| Type | Generated Code | Description |
|------|----------------|-------------|
| Class | `classNameSerialize(value)` + `static serialize(value)` | Standalone function + static wrapper method |
| Enum | `enumNameSerialize(value)`, `enumNameSerializeWithContext` | Standalone functions |
| Interface | `interfaceNameSerialize(value)`, etc. | Standalone functions |
| Type Alias | `typeNameSerialize(value)`, etc. | Standalone functions |
## Cycle Detection Protocol
The generated code handles circular references using `__id` and `__ref` markers:
```json
{
"__type": "User",
"__id": 1,
"name": "Alice",
"friend": { "__ref": 2 } // Reference to object with __id: 2
}
```
When an object is serialized:
1. Check if it's already been serialized (has an `__id`)
2. If so, return `{ "__ref": existingId }` instead
3. Otherwise, register the object and serialize its fields
## Type-Specific Serialization
| Type | Serialization Strategy |
|------|------------------------|
| Primitives | Direct value |
| `Date` | `toISOString()` |
| Arrays | For primitive-like element types, pass through; for `Date`/`Date | null`, map to ISO strings; otherwise map and call `SerializeWithContext(ctx)` when available |
| `Map<K,V>` | For primitive-like values, `Object.fromEntries(map.entries())`; for `Date`/`Date | null`, convert to ISO strings; otherwise call `SerializeWithContext(ctx)` per value when available |
| `Set<T>` | Convert to array; element handling matches `Array<T>` |
| Nullable | Include `null` explicitly; for primitive-like and `Date` unions the generator avoids runtime `SerializeWithContext` checks |
| Objects | Call `SerializeWithContext(ctx)` if available (to support user-defined implementations) |
Note: the generator specializes some code paths based on the declared TypeScript type to
avoid runtime feature detection on primitives and literal unions.
## Field-Level Options
The `@serde` decorator supports:
- `skip` / `skipSerializing` - Exclude field from serialization
- `rename = "jsonKey"` - Use different JSON property name
- `flatten` - Merge nested object's fields into parent
## Example
```typescript before
/** @derive(Serialize) */
class User {
id: number;
/** @serde({ rename: "userName" }) */
name: string;
/** @serde({ skipSerializing: true }) */
password: string;
/** @serde({ flatten: true }) */
metadata: UserMetadata;
}
```
```typescript after
import { SerializeContext } from 'macroforge/serde';
class User {
id: number;
name: string;
password: string;
metadata: UserMetadata;
/** Serializes a value to a JSON string.
@param value - The value to serialize
@returns JSON string representation with cycle detection metadata */
static serialize(value: User): string {
return userSerialize(value);
}
/** @internal Serializes with an existing context for nested/cyclic object graphs.
@param value - The value to serialize
@param ctx - The serialization context */
static serializeWithContext(value: User, ctx: @{SERIALIZE_CONTEXT}): Record<string, unknown> {
return userSerializeWithContext(value, ctx);
}
}
/** Serializes a value to a JSON string.
@param value - The value to serialize
@returns JSON string representation with cycle detection metadata */ export function userSerialize(
value: User
): string {
const ctx = @{SERIALIZE_CONTEXT}.create();
return JSON.stringify(userSerializeWithContext(value, ctx));
} /** @internal Serializes with an existing context for nested/cyclic object graphs.
@param value - The value to serialize
@param ctx - The serialization context */
export function userSerializeWithContext(
value: User,
ctx: SerializeContext
): Record<string, unknown> {
const existingId = ctx.getId(value);
if (existingId !== undefined) {
return { __ref: existingId };
}
const __id = ctx.register(value);
const result: Record<string, unknown> = { __type: 'User', __id };
result['id'] = value.id;
result['userName'] = value.name;
{
const __flattened = userMetadataSerializeWithContext(value.metadata, ctx);
const { __type: _, __id: __, ...rest } = __flattened as any;
Object.assign(result, rest);
}
return result;
}
```
## Required Import
The generated code automatically imports `SerializeContext` from `macroforge/serde`.