JSON Patterns
Version note: The
json%elaboration syntax lives inLean.Data.Json.Elab. Antiquotation and object-key rules may evolve across toolchain versions; verify against the current source if behavior differs from what is documented here.
Scope
Reference for constructing JSON values in Lean 4 using json% elaboration syntax, Json.mkObj, and ToJson instances. Useful for any Lean code that constructs JSON — scripts, plugins, metaprograms, tooling.
Read when: building JSON payloads, interpolating Lean values into JSON, deriving or using ToJson, or debugging json% elaboration errors.
Not part of the prove/autoprove default loop. This is supplemental reference material for projects that produce JSON output.
When to Use
- Constructing JSON payloads in Lean code
- Using
json%elaboration syntax - Deriving or using
ToJsoninstances - Debugging
json%syntax or elaboration errors
Quick Start
- Import
Lean.Data.Json(or at leastLean.Data.Json.ElabandLean.Data.Json.FromToJson). - Build a static skeleton with
json%{...}orjson%[...]. - Interpolate computed values with
$expr(requiresToJsonforexpr's type). - Keep object keys static (
identor string literal); switch toJson.mkObjfor dynamic keys. - Inspect output with
#eval j.prettyor#eval j.compress.
import Lean.Data.Json
open Lean
def payload (user : String) (scores : Array Nat) : Json :=
json%{
user: $user,
scores: $scores,
active: true,
"meta": {"source": "cli", "version": 1}
}Stdlib Semantics
Ground truth from Lean/Data/Json/Elab.lean:
json% nullelaborates toLean.Json.null.json% true/json% falseelaborate toLean.Json.bool ....- String and numeric literals elaborate to
Lean.Json.str/Lean.Json.num. - Arrays elaborate recursively to
Lean.Json.arr #[...]. - Objects elaborate to
Lean.Json.mkObj [...]. BecausemkObjbuilds aStd.TreeMap, duplicate keys collapse (last value wins) and output order is map order, not insertion order. - Object keys accept either
identor string literal. Keys that are Lean keywords must be quoted.identkeys are converted withName.toString.- String keys are preserved as written.
- Antiquotation uses
Lean.toJson:json%{x: $expr}elaborates viatoJson expr.- Top-level antiquotation works too:
json% $exprelaborates totoJson expr. Ifexpr : Json, this embeds it unchanged (ToJson Json = id). - Missing
ToJsoninstance causes elaboration failure.
ToJson FloatserializesNaNand±Infinityas JSON strings, not numbers.
Patterns
Interpolate structured data with ToJson
import Lean.Data.Json
open Lean
structure User where
name : String
age : Nat
deriving ToJson
def envelope (u : User) : Json :=
json%{"kind": "user", "payload": $u}Dynamic keys with Json.mkObj
json% does not support antiquotation in key position. Build dynamic-key objects manually.
import Lean.Data.Json
open Lean
def singletonObj [ToJson α] (k : String) (v : α) : Json :=
Json.mkObj [(k, toJson v)]Static skeleton + dynamic fields with Json.mergeObj
Combine a json% skeleton with dynamic fields built separately.
import Lean.Data.Json
open Lean
def annotated (base : Json) (tag : String) : Json :=
base.mergeObj (Json.mkObj [("tag", toJson tag)])Optional fields with Json.opt
Json.opt emits nothing for none and a key-value pair for some, avoiding explicit branching.
import Lean.Data.Json
open Lean
def withOptional (name : String) (tag? : Option String) : Json :=
Json.mkObj ([("name", toJson name)] ++ Json.opt "tag" tag?)Mix static and computed values
import Lean.Data.Json
open Lean
def stats (count : Nat) (ok : Bool) : Json :=
json%{
count: $count,
ok: $ok,
ratio: $(if count == 0 then 0.0 else 1.0),
tags: ["lean", "json"]
}Failure Modes and Fixes
unsupported syntaxaroundjson%:- Ensure JSON fragments are valid
jsonsyntax, not arbitrary Lean terms. - Wrap Lean expressions as
$expr.
- Ensure JSON fragments are valid
failed to synthesize ToJson ...:- Add/derive
ToJsonfor the interpolated type. - Convert to a supported type before interpolation.
- Add/derive
- Fields reordered in
pretty/compress:- This is expected. Objects are stored in
TreeMaporder, not insertion order. Do not rely on field ordering.
- This is expected. Objects are stored in
- Key-related parse issues:
- Use
ident: valueor"string key": value. - Keys that are Lean keywords (e.g.
meta,where,import) must be quoted as string literals. - For computed keys, stop using
json%object syntax and useJson.mkObj.
- Use
See Also
- lean4-custom-syntax — if building new syntax that emits JSON
- metaprogramming-patterns — if building elaborators that produce JSON