Value Formatting
How each scalar, string, and key is written so that it reads back unchanged.
A writer MUST preserve a value's type, not merely its printed appearance. 42 and 42n print similarly and mean different things; a string that looks like a number must not read back as a number. This page fixes the written form of every value kind.
Type-preserving scalars
number
42, 3.14, -7
plain literal
bigint
42n
the n suffix is required, or it reads back as a number
decimal
3.14m
the m suffix is required, or it reads back as a number
boolean
T / F
true / false are also valid
null
N
null is also valid
datetime
dt"2024-03-20T14:30:00.000Z"
also d"…" (date) and t"…" (time)
binary
b"SGVsbG8="
base64 payload
special numbers
Inf, -Inf, NaN
the literals the grammar defines
b: 42n, d: 3.14m, t: T, f: F, n: N, dt: dt"2024-03-20T14:30:00.000Z"A member may declare a presentation option that selects a spelling — format on the numeric and string types, plus encloser and escapeLines on strings. These are write-only: a writer MUST honor them, and a reader MUST NOT enforce them.
A numeric value carrying a radix format (hex, octal, binary) is written in that base with its base prefix and its type suffix — 0xffn, not ff — so that the output still reads back as the same typed value. A format selects a base, never a type, and never licenses output that the member's own schema would reject. A sign is written before the base prefix (-0xff). A value with a fractional part has no radix literal, so a radix format does not apply to it and the decimal spelling is written instead.
Where no schema is available, a writer chooses the spelling itself, under one rule: infer only what the value evidences. Any spelling that re-parses to an equal value is permitted; a writer MUST NOT guess a form the value does not support. A notation such as hex is not part of the value — 0xff and 255 parse identically — so a schema-less number is written in decimal.
Temporal kind
The three temporal types share one value — an instant — but not one literal. A writer MUST NOT normalize everything to dt"…"; it selects the literal in this order:
A declared type wins. Under a schema,
datewritesd"…",timewritest"…", anddatetimewritesdt"…". The kind is a type name, not a format, so it is never inferred when it has been declared.Otherwise, infer from the value — under the same "infer only what the value evidences" rule above. An instant whose time component is entirely zero evidences a
date; one whose date component is the time-only sentinel1900-01-01evidences atime; anything else is adatetime.
Inference is value-preserving but not text-preserving: a datetime that happens to fall at midnight is written d"…", and one on 1900-01-01 is written t"…". Each re-parses to the very same instant, so round-trip holds — only the spelling may differ from the input text. Declare the type when the spelling matters.
Strings
A string is written in the leanest form that reads back as the same string. A writer chooses among three forms:
open
John
the default — no quoting needed
regular
"John"
the value would otherwise be ambiguous or malformed
raw
r"C:\path"
the value contains characters that would need heavy escaping
A string MUST be quoted when leaving it open would change how it reads back. There are two ways that happens, and both are the writer's responsibility: the text may read back as a different value, or it may fail to read back at all.
That is the case when the string:
is empty;
looks like a number —
3.14,007,-5,.5;carries a base prefix and does not decode —
0x123FG,0b. A base prefix announces a base, so a bare0x123FGis read asinvalid-numberrather than as text. Quoting is what tells the reader this is a string, and it is the only thing that can. A run carrying no marker is not in this class however numeric it looks —1eand1.23ee4read back as themselves and are written bare;is a keyword —
T,F,N,true,false,null;looks like a date or time —
2024-03-20,14:30:00;contains a comma, or a structural character that would end the value;
contains a section separator (
---), which would otherwise split the document;has leading or trailing whitespace, which an open string loses on re-parse.
Ordinary codes and identifiers need none of this. A writer emits them bare, because they read back as themselves:
The test is precise on purpose: quote a string when the bare text would read back as a number (Rule 1) or as an error (Rule 2), and not otherwise. Quoting everything that begins with a digit is safe but wrong for a format whose output is meant to be lean — and it hides tokenizer defects, because a quoted value never exercises the path a bare one takes.
A string containing characters that would need heavy escaping — a literal newline, many backslashes — is written as a raw string instead: r"line1 line2".
Known gap. A string beginning with
@or$currently cannot exist as data at all: the reference parser resolves it as a variable or schema reference even when quoted ("@ref",r'@ref'), and fails. The parser fix is tracked with Parsing & Errors; once representable, such strings MUST be written quoted.
Keys
When Key Emission decides a key is written, the key itself follows the same principle: it MUST be quoted whenever writing it bare would not read back as that key.
A key is written bare only if it is an identifier-like open string. It MUST be quoted when it:
is numeric —
"5","3.14";is a keyword —
"null","true","false","N","T","F";contains a colon, comma, brace, bracket, or quote —
"a:b";contains a section separator (
---) —"a---b";has leading or trailing whitespace.
Keys drawn from foreign data — imported JSON, locale tags such as
code:en— routinely contain colons. A writer that emits them bare produces output that cannot be re-parsed.
Containers
Arrays are written
[ … ], elements formatted by these same rules. An array whose element type is a schema writes each element positionally.Objects are written
{ … }and are always enclosed — only a top-level record may use the open form. See Record & Document Output.An untyped object (a member declared
objectwith no shape) has no schema to recover its member names from, so its members are written keyed.
See Also
Key Emission — whether a key is written at all
Last updated
Was this helpful?
