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; }