Skip to content

Messages

Messages are the primary data structures in IonPath. They define named, indexed field collections that are serialized to CBOR for wire transmission.

Basic Syntax

Declare a message with the msg keyword:

msg User {
    id: u4;
    name: string;
    email: string;
}

Each field has a name and a type, separated by a colon and terminated with a semicolon.

Field Indexing

Fields are automatically assigned sequential indices starting from 0. These indices determine the wire identity of each field — they are used as CBOR array positions during serialization.

msg User {
    id: u4;       // index 0
    name: string;   // index 1
    email: string;  // index 2
}

Reordering fields is a breaking change. The field index determines its position in the CBOR array. If you reorder fields, existing clients that read index 0 as id would now read a different field. Use schema lock to detect this.

Optional Fields

Append ? to make a field optional. It wraps the type in Maybe<T>:

msg UserProfile {
    id: u4;
    name: string;
    bio: string?;       // nullable — Maybe<string>
    avatarUrl: uri?;   // nullable — Maybe<Uri>
}

In C# this generates IonMaybe<string>; in TypeScript, string | null.

Array Fields

Append [] for variable-length arrays:

msg SearchResult {
    query: string;
    results: User[];     // array of User messages
    scores: f4[];         // array of floats
}

Partial Fields

The ~ modifier creates a partial/sparse version of a type where only modified fields are serialized:

service UserService() {
    UpdateUser(id: u4, patch: ~User): User;
}

In C# this generates IonPartial<User> which tracks which fields have been set using expression trees.

Referencing Other Messages

Messages can reference other messages as field types:

msg Address {
    street: string;
    city: string;
    zip: string;
}

msg Company {
    name: string;
    address: Address;           // nested message
    branches: Address[];         // array of nested messages
    headquarters: Address?;      // optional nested message
}

Forward references are supported — you can reference a message that is declared later in the file or in a different module (imported with #use).

Wire Format

Messages are encoded as CBOR arrays with a fixed length. Each element at index i corresponds to the field at index i:

// Ion
msg Point { x: f4; y: f4; }

// CBOR wire representation (conceptual)
[1.5, 2.7]   // index 0 = x, index 1 = y

Generated Code

A message like:

msg Vector { x: f4; y: f4; z: f4; }

Generates in C#:

public sealed class Vector
{
    public float x { get; set; }
    public float y { get; set; }
    public float z { get; set; }
}

And in TypeScript:

export interface Vector {
    x: number;
    y: number;
    z: number;
}