Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Round-Trip Fidelity

Understanding which conversion paths preserve data perfectly and where information can be lost.

Round-Trip Matrix

PathData PreservedLost
.tl.tlbx.tlAll data and schemasComments, formatting
.tl.json.tlBasic types (string, number, bool, null, array, object)Schemas, comments, refs, tags, maps, timestamps, bytes
.tl.tlbx.jsonSame as .tl.jsonSame losses
.json.tl.jsonAll JSON-native types(generally lossless)
.json.tlbx.jsonAll JSON-native types(generally lossless)
.tlbx.tlbx (recompile)All data(lossless)

Lossless: Text ↔ Binary

The text-to-binary-to-text round-trip preserves all data and schema information:

tealeaf compile original.tl -o compiled.tlbx
tealeaf decompile compiled.tlbx -o roundtrip.tl
tealeaf compile roundtrip.tl -o roundtrip.tlbx
# compiled.tlbx and roundtrip.tlbx contain equivalent data

What’s lost:

  • Comments (stripped during compilation)
  • Whitespace and formatting
  • The decompiled output may have different formatting than the original

What’s preserved:

  • All schemas (@struct definitions)
  • All values (every type)
  • Key ordering
  • Schema-typed data (table structure)

Lossy: TeaLeaf → JSON

JSON cannot represent all TeaLeaf types. The following conversions are one-way:

Timestamps → Strings

created: 2024-01-15T10:30:00Z

JSON output:

{"created": "2024-01-15T10:30:00.000Z"}

Reimporting: the ISO 8601 string becomes a plain String, not a Timestamp.

Maps → Arrays

headers: @map {200: "OK", 404: "Not Found"}

JSON output:

{"headers": [[200, "OK"], [404, "Not Found"]]}

Reimporting: becomes a plain nested array, not a Map.

References → Objects

!ref: {x: 1, y: 2}
point: !ref

JSON output:

{"point": {"$ref": "ref"}}

Reimporting: becomes a plain object with $ref key, not a Ref.

Tagged Values → Objects

event: :click {x: 100, y: 200}

JSON output:

{"event": {"$tag": "click", "$value": {"x": 100, "y": 200}}}

Reimporting: becomes a plain object, not a Tagged.

Bytes → Hex Strings (JSON only)

Bytes round-trip losslessly within TeaLeaf text format using b"..." literals:

data: b"cafef00d"

However, JSON export converts bytes to hex strings:

{"data": "0xcafef00d"}

Reimporting from JSON: becomes a plain string, not bytes.

Schemas → Lost

@struct user (id: int, name: string)
users: @table user [(1, alice), (2, bob)]

JSON output:

{"users": [{"id": 1, "name": "alice"}, {"id": 2, "name": "bob"}]}

The @struct definition is not represented in JSON. However, from-json can re-infer schemas from uniform arrays.

Bytes and Text Format

Bytes now round-trip losslessly through text format using the b"..." literal:

Binary (bytes value) → Decompile → Text (b"..." literal) → Compile → Binary (bytes value)

The decompiler emits b"cafef00d" for bytes values, and the parser reads them back as Value::Bytes.

Ensuring Lossless Round-Trips

Use Binary for Storage

If you need to preserve all TeaLeaf types (refs, tags, maps, timestamps, bytes), keep data in .tlbx:

# Lossless cycle
tealeaf compile data.tl -o data.tlbx
tealeaf decompile data.tlbx -o data.tl
# data.tl preserves all types (except comments)

Use JSON Only for Interop

JSON conversion is for integrating with JSON-based tools. Don’t use it as a primary storage format if your data uses TeaLeaf-specific types.

Verify with CLI

# Compile → JSON two ways, compare
tealeaf to-json data.tl -o from_text.json
tealeaf compile data.tl -o data.tlbx
tealeaf tlbx-to-json data.tlbx -o from_binary.json
# from_text.json and from_binary.json should be identical

Type Preservation Summary

TeaLeaf TypeBinary Round-TripJSON Round-Trip
NullLosslessLossless
BoolLosslessLossless
IntLosslessLossless
UIntLosslessLossless (as number)
FloatLosslessLossless
StringLosslessLossless
BytesLosslessLossy (→ hex string)
ArrayLosslessLossless
ObjectLosslessLossless
MapLosslessLossy (→ array of pairs)
RefLosslessLossy (→ $ref object)
TaggedLosslessLossy (→ $tag/$value object)
TimestampLosslessLossy (→ ISO 8601 string)
SchemasLosslessLost (re-inferred on import)
CommentsLost (stripped)Lost