On this page· 4

Specification

4. Body: JSONL records

4.1 Lines

  • The body is a sequence of lines, each one JSON object [RFC8259] encoded as UTF-8 and terminated by one LF.
  • A writer MUST NOT emit empty lines, CR characters outside JSON strings, or a line containing a raw LF inside a JSON value (JSON strings escape LF as \n).
  • A reader MUST reject a body that is not valid UTF-8 with invalid_utf8.
  • A reader MUST skip empty lines and MUST strip one trailing CR before parsing a line. Skipped lines do not count as records.
  • The marker line (§2) is not a record. It MUST appear only as the last line of the text section and only when an appendix exists; anywhere else it is invalid_record.
  • records in the header and record_count in the footer count the record lines.
  • A writer MUST NOT emit a JSON object with duplicate member names. A reader that meets one MUST either reject the line with json, or keep the last value for the name; the reference reader keeps the last value (serde_json behaviour, measured in §10.4). Conformance tests do not exercise duplicates.

4.2 The record envelope

Every record line is a JSON object with:

  • k (REQUIRED): a JSON string, the record kind. A line without a string k MUST be rejected with invalid_record.
  • v (OPTIONAL): a non-negative JSON integer, the per-kind format version. Absent means 1.
  • the kind's own members (§5).

A record is typed when its kind is in the typed registry (§5.1) and its v is at most the highest version this reader supports for that kind. Every typed kind is at version 1. Otherwise the record is opaque.

4.3 Typed records

  • A reader MUST parse a typed record against its schema (§5.1). A schema failure (missing REQUIRED member, wrong JSON type) MUST be reported as json.
  • Members the schema does not name MUST be preserved in the record's extra map and re-emitted after the declared members.
  • A writer MUST emit a typed record as: k first, then v, then the declared members in schema order, then extra members in the order they were read. OPTIONAL members whose value is absent are omitted, except those marked "always emitted" in §5.1.
  • Re-emitting a typed record normalises its values: a 32-bit float member (for example salience, confidence) is re-emitted as the shortest decimal that round-trips the 32-bit value, and whitespace, escapes and number spelling follow §10.2. A typed round trip therefore preserves the value but MAY change the bytes.

4.4 Opaque records (forward compatibility)

A record MUST be kept opaque, not rejected, when either:

  1. its kind is not in the typed registry, including the reserved kinds of §5.2 and any future kind; or
  2. its kind is typed but its v is higher than this reader supports.

For an opaque record, a reader MUST keep the whole JSON object, including k and v, and a writer MUST re-emit it with the same members, the same values, and the members in the order they were read (§10.1). This is the opaque round-trip rule. It guarantees JSON value equality and member order; it does not guarantee byte equality, because escapes and number spellings are re-emitted in the form of §10.2 (a JSON integer outside the 64-bit range, for example, becomes a binary64 number; §10.4).

Opaque records take part in sorting, merge and diff through the identity rules of §5.3.

© 2020-2026 Cintile Inc. All Rights Reserved.

Anyone may implement this format. Copying or republishing the text of this specification requires permission from Cintile Inc.

Sections