@json / @@json
The @json attributes describe how a class looks as JSON, so you don’t have to write a baml.ToJson or baml.FromJson override. baml.json follows them with no extra arguments, and so do the serde impls of a generated Rust SDK.
@alias is different: it’s the name a model is shown. Use @json.rename for the key your API uses.
@json.rename
Sets the JSON key of a field, or the JSON string of an enum variant.
The field reads from vpc_id and writes to vpc_id. The name vpcId is no longer a JSON key. If a field has both @alias and @json.rename, its JSON key is the @json.rename, and a model’s answer may use either.
@json.flatten
Goes on a map<string, json> field. The field holds every key no other field took, and writes them next to the other fields.
{"id": "a", "region": "us"} reads as Config { id: "a", extra: {"region": "us"} } and writes back the same way. If the map holds a key a declared field also writes, the declared field wins and the map entry is skipped.
@json.fallback
Goes on a map<string, json> field, and keeps a union open to members you don’t know yet. An object whose tag no member has becomes this class, with the whole object in the field. It writes that object back and nothing else.
The class’s other fields must be literals, and they name the tag: type below. Every other member has to have that literal field too.
Only the tag decides. Once a tag picks a member, anything else wrong with the object is an error, including another literal field holding the wrong value. If several members share a tag value, the next literal field that tells them apart is read the same way:
Here {"type": "storage", "service": "minio"} is a BindingOther, and {"type": "storage"} is an error.
@json.flatten and @json.fallback shape JSON in baml.json and generated serde. baml.sap, which parses a model’s answer, doesn’t read them yet, so don’t use them on a class a model fills in.
@@json.omit_nulls
Leaves out the class’s optional fields that hold null when it’s written. A field is optional when its declared type has a null arm, directly or through an alias. A json field keeps its null, because there null is a value, and so does a field of a generic type T set to string?. Reading doesn’t change: a missing optional field reads as null. It applies only to the class’s own fields, not to nested classes.
Request { name: "a", note: null } writes {"name": "a"}.
What the compiler rejects
The compiler checks unions in class fields, type aliases, and function parameters and return types. A union spelled only in an expression, such as baml.json.decode<A | B>(text), is checked when it runs, and fails with a DecodeError.
reflect classes copied with fields_from keep these attributes. An enum rebuilt with reflect.Enum.new doesn’t keep @json.rename.