Skip to content

Tutorial: Unified Write — overrides and field selection

Time: ~10 minutes · Prerequisite: Quick start or C# guide

Use the language switcher for 한국어.


When to use this

You have one large in-memory message (chat, notification, state snapshot) and must send it to many recipients. Only a few fields differ per recipient (display name, read flag, locale-specific text). Cloning the whole object N times is expensive.

DeukPack generates a Pack(format, fieldIds, overrides?) method on every C# struct: pass null for fieldIds to send all fields, and an optional Dictionary<int, object> for per-field replacement values — without mutating or cloning the message.


1. Field IDs in IDL

Thrift / DeukPack structs use numeric field IDs:

struct ChatMessage {
    > 1 msgId;
    > 2 body;
    > 3 displayNameForRecipient;  // override per user
    > 4 isReadForRecipient;
}

After npx deukpack ... --csharp, the generated class includes a nested FieldId class with named constants for every field. Use those instead of bare numbers — if the IDL changes, the compiler catches mismatches immediately.


2. C# — overrides (all fields)

var msg = BuildMessageOnce(); // heavy work once

var overrides = new Dictionary<int, object>
{
    { ChatMessage.FieldId.DisplayNameForRecipient, "Alice (you)" },
    { ChatMessage.FieldId.IsReadForRecipient, false }
};
byte[] bin = msg.Pack(DpFormat.Binary, null, overrides);
network.Send(bin);

Rules:

  • Keys = use StructName.FieldId.PropertyName (generated const int). Never hard-code numbers.
  • Values = same CLR types as the generated properties (wrong type → cast exception).
  • If overrides is null or empty, behavior matches standard Pack().
  • For a field whose type is another struct or list/map, pass a whole instance of that type (or the whole collection). There is no nested path API to tweak inner members only. See core DEUKPACK_WRITE_WITH_OVERRIDES_API.md §1.3.

Full API tables and C++/JS: API reference · core repo DEUKPACK_WRITE_WITH_OVERRIDES_API.md.


3. JavaScript (--js)

Generated js/generated_deuk.js adds pack, unpack, and FieldId on each struct helper (v1.7.6+ standard; toBinary/toJson are deprecated aliases):

var F = ChatMessage.FieldId;
// pack to JSON with overrides
var json = ChatMessage.pack(msg, 'json', null, {
  [F.DisplayNameForRecipient]: "Bob",
  [F.IsReadForRecipient]: true
});
// pack to binary
var bin = ChatMessage.pack(msg, undefined, null, {
  [F.DisplayNameForRecipient]: "Bob"
});

4. C++

Each struct gets kFieldId_* constants aligned with C#/JS. Use the generated pack/binary (or Thrift interop) write path next to your types; there is no separate Pack step in current emit.

See API reference.


5. Field selection

Pack(format, fieldIds, overrides?) serializes only the fields you list when fieldIds is non-null. No partial types or manual field copying.

var full = LoadFullUser();
byte[] bin = full.Pack(DpFormat.Binary, new[] {
    UserRecord.FieldId.DisplayName,
    UserRecord.FieldId.Level,
    UserRecord.FieldId.AvatarUrl
});

Combine selection with overrides:

byte[] bin2 = full.Pack(DpFormat.Binary, new[] {
    UserRecord.FieldId.DisplayName,
    UserRecord.FieldId.Level
}, new Dictionary<int, object> {
    { UserRecord.FieldId.DisplayName, r.LocalizedName }
});

JavaScript — same signature shape:

var F = UserRecord.FieldId;
var ids = [F.DisplayName, F.Level];
var json = UserRecord.pack(full, 'json', ids, null);
var json2 = UserRecord.pack(full, 'json', ids, { [F.DisplayName]: r.LocalizedName });

6. struct extends — inheritance

Use extends in IDL to inherit parent fields. The code generator flattens parent fields into the child as a single type. Field ID collisions cause a build error.

struct UserBase {
    > 1 id;
    > 2 displayName;
}

struct UserFull extends UserBase {
    > 3 level;
    > 4 avatarUrl;
}

The generated UserFull has all 4 fields (1–4). Multi-level inheritance (A → B → C) is supported.


Different problem: hide whole fields per client profile → wireProfiles annotation and --wire-profile. See Wire profile subset.


8. Feature comparison

Overrides (all fields) Field subset Wire Profile extends
Purpose All fields, some values replaced Only selected fields Build-time subset type Common field inheritance
Decision time Runtime Runtime Build time IDL definition
API surface Write(oprot, null, overrides) Write(oprot, fieldIds, overrides?) With all features
Dynamic Yes Yes No

9. Sample in the core repo


Next steps