ANY DEFINED BY
The compiler accepts explicit type mappings for ANY DEFINED BY members.
The mappings are written into the existing AnyDefinedByType.choices
field of the .asni format, which the runtimes already understand.
No file-format version change is needed.
An ANY DEFINED BY member has the type selected by another member of the
same SEQUENCE or SET. ASN.1 does not supply the mapping from selector
values to types; that normally comes from the protocol specification.
Without a mapping, the runtime treats the member as raw encoded bytes.
With a mapping, its value has the representation of the selected type.
Command line
asnkitcc compile --any-defined-by choices.toml -o protocol.asni protocol.asn
asnkitcc generate --any-defined-by choices.toml --plugin python -o out protocol.asn
asnkitcc generate --any-defined-by choices.toml --plugin rust -o src protocol.asn
--any-defined-by can be repeated to read several TOML files. All ASN.1
modules used by the mappings must be supplied in the compilation. The
option is for compile and generate, not parse or convert.
To convert with mappings, compile an .asni first and pass it to
asnkitcc convert. Generation gives the plugin the mapped .asni and
the original .asnst; generated models still use dynamic values for
an ANY DEFINED BY, with dispatch performed by the embedded schema.
For example:
Example DEFINITIONS ::= BEGIN
Message ::= SEQUENCE {
kind INTEGER { text(1), number(2) },
payload ANY DEFINED BY kind
}
END
[[any_defined_by]]
type = "Example.Message"
member = "payload"
[any_defined_by.choices]
text = "UTF8String"
number = "INTEGER"
For kind = 1, payload is a string; for kind = 2, it is an integer.
Other selector values are rejected by encoding and decoding. To keep
raw bytes for a specific known value, map that value to OpenType.
TOML format
Configuration files are UTF-8 TOML. The root accepts only
any_defined_by, an array of tables. Each [[any_defined_by]] starts a
new mapping. A file may contain several mappings; an empty file (or
any_defined_by = []) adds none. Unknown fields at the root or inside a
mapping are errors.
Each [[any_defined_by]] entry has three required fields:
| Field | TOML type | Meaning |
|---|---|---|
type |
String | The containing type assignment as Module.Type, with the exact ASN.1 case. |
member |
String | A member name, or a dotted path through inline constructed types. |
choices |
Table of string values | A nonempty mapping from selector values or ASN.1 value names to type names. |
Table styles and quoting
Both an inline table and a table section are supported. These entries illustrate the two styles for different target members:
[[any_defined_by]]
type = "Messages.Message"
member = "payload"
choices = { "1" = "UTF8String", "2" = "INTEGER" }
[[any_defined_by]]
type = "Messages.Batch"
member = "entries.parameters"
[any_defined_by.choices]
"1.3.6.1.4.1.99999.1" = "UTF8String"
"Payloads.id-point" = "Payloads.Point"
[any_defined_by.choices] belongs to the most recent
[[any_defined_by]] entry. Put type and member before the choices
section: fields written afterward remain inside that table until another
header starts. Do not define choices both inline and as a section for
the same entry.
Quote keys containing dots, including literal OIDs and qualified ASN.1
names. Without quotes, TOML reads Payloads.id-point as nested tables,
not as one selector key. Plain keys such as text, id-text, TRUE, and
FALSE are allowed; TOML keys are strings even when they look like a
number or boolean. Quoting numeric keys is recommended for clarity.
All choice values must be strings naming types: "NULL", not TOML's
boolean false or an unquoted word. Single-quoted and double-quoted TOML
strings both work. # starts a comment outside a string.
Member paths
A path such as content.parameters enters inline members. A SEQUENCE
OF or SET OF element consumes no path component. Extension addition
groups are traversed by their member names. If a path reaches a named
type reference, configure that type's assignment directly; this avoids
silently changing other uses or specializing a referenced type.
Selector keys
Selector keys are strings in TOML:
| Defining member | Keys |
|---|---|
| INTEGER | Decimal integers, named numbers, or assigned INTEGER value names. Selectors must fit signed 128 bits. |
| ENUMERATED | Item names or assigned values of that enumeration. Both runtime name and numeric-enum modes are supported. |
| OBJECT IDENTIFIER | Dotted decimal OIDs, or assigned OBJECT IDENTIFIER value names. |
| BOOLEAN | TRUE or FALSE. |
Unqualified assigned names are looked up in the module of type,
including its imports. Qualified names such as Algorithms.id-rsa are
also accepted. Named INTEGER numbers and ENUMERATED items come from the
defining member's type. OID assignments may reference other OID values
and use named arcs. Different keys resolving to the same selector value
are rejected.
Choice types
A choice type is an assigned type (local, imported, or Module.Type),
a built-in type requiring no component definition, such as NULL,
INTEGER or OCTET STRING, or OpenType. Structured or parameterized
types should be assigned/instantiated in ASN.1 and referred to by name.
The selected type retains its tags and constraints. Tags written on the
ANY DEFINED BY member itself are retained too.
Combining files and diagnosing errors
asnkitcc compile \
--any-defined-by messages.toml --any-defined-by batch.toml \
-o messages.asni payloads.asn messages.asn
Mappings are appended in command-line file order. Files do not
override earlier entries or merge their choices tables. Configure a
particular (type, member) pair once across all files. For example,
putting one choice for Messages.Message.payload in each of two files
is a duplicate target error; combine those choices into one entry.
Paths supplied to the CLI are relative to its working directory. The TOML contains ASN.1 names, not file paths, and has no include directive. All schema files still need to be supplied to the compiler.
| Problem | Correction |
|---|---|
default = "OpenType" |
Remove it; fallbacks are not implemented. Map specific known selectors to OpenType if raw bytes are intended. |
choices = { "1" = 2 } |
Use a quoted type name, such as "INTEGER". |
Unquoted 1.2.3 or Module.value key |
Quote the entire key to prevent nested TOML tables. |
Empty choices |
Supply at least one mapping, or remove the whole entry to retain unconfigured ANY behavior. |
Two keys such as "1" and a named number resolving to 1 |
Keep one; duplicate detection uses resolved selector values. |
| A path enters a named type | Target that type's own Module.Type assignment and member path. |
| Unknown selector when encoding or decoding | Add the protocol's mapping and recompile, or correct the message's selector. |
The compiler rejects unknown TOML fields, missing types or members, non-ANY DEFINED BY targets, unsupported selector types, duplicate target entries, duplicate selector values, and empty choice tables. Errors identify the configured type and member; TOML parse errors also identify the configuration file in the CLI.
For an AlgorithmIdentifier-style schema, a configuration can use:
[[any_defined_by]]
type = "PKIX1Explicit88.AlgorithmIdentifier"
member = "parameters"
[any_defined_by.choices]
"1.2.840.113549.1.1.1" = "NULL"
"PKIX1Algorithms88.id-ecPublicKey" = "PKIX1Algorithms88.ECParameters"
Supply the ASN.1 files defining all these assignments.
Runnable examples
The repository's
examples/any_defined_by
contains complete, matching files:
| Files | Purpose |
|---|---|
messages.asn, payloads.asn |
A small protocol and imported payload types/OID assignments. |
messages.toml |
INTEGER, ENUMERATED and BOOLEAN mappings, using both table styles. |
batch.toml |
Imported and qualified OID values, a literal OID, and a path through inline collection elements. |
message.json, event.json, flagged.json, batch.json |
JER messages with typed payloads, ready to encode. |
raw.der.hex |
A known selector mapped to OpenType, carrying encoded DER NULL. |
README.md |
Compilation, conversion, expected bytes, and model generation commands. |
The commands in that README run from the repository root and place
output in target/any-defined-by-example. They use two repeated
--any-defined-by options and show why compiling before conversion
matters. No external ASN.1 specifications are needed.
Rust compiler library
Existing entry points keep their behavior. Use CompileOptions with
compile_with_options, compile_to_asni_with_options, or
compile_to_asni_with_summary_and_options for mappings:
use asnkit_compiler::{CompileOptions, Input, compile_to_asni_with_options};
use asnkit_compiler::container::Protection;
let options = CompileOptions::from_toml(&std::fs::read_to_string("choices.toml")?)?;
let input = Input::String(std::fs::read_to_string("protocol.asn")?);
let asni = compile_to_asni_with_options(
&input, None, None, &Protection::default(), &options,
)?;
std::fs::write("protocol.asni", asni)?;
Callers can also construct AnyDefinedBy entries directly using
type_name, member, and a BTreeMap<String, String> of choices.
To combine configuration files, append their any_defined_by vectors;
compilation checks for duplicates across the combined configuration.
Runtime behavior and limitations
Load the compiled file with the ordinary runtime API; no new load option
is needed. An OpenType choice uses raw bytes and is not supported in
JER/XER. Raw bytes remain specific to their encoding, so they cannot be
blindly transcoded across codecs.
Choice constraints are preserved in the compiled schema. The existing runtime constraint walkers do not automatically dispatch through an ANY DEFINED BY member; check the selected type explicitly when needed.
Use a defining member that is available before the dependent member when decoding. The existing runtimes do not provide a general deferred resolution mechanism for selectors that appear later in a message.
Deferred work
Fallback types (default = "OpenType", for example) and runtime
load-time mapping overrides are not implemented. The compiler
rejects default rather than accepting it without effect. Version 1.0
of the node format has no fallback field; adding one requires a format
and runtime change. The JavaScript compiler wrapper does not yet expose
mapping options; use the CLI or Rust compiler library.