Generating JSON from Bosatsu Values
Bosatsu can be used as a typed configuration language and then rendered to JSON with lib json.
This page focuses on lib json (not tool json).
Quick start
- Typecheck as you iterate:
./bosatsuj lib check --name core_alpha
- Generate JSON for a value:
./bosatsuj lib json write --name core_alpha --main Bosatsu/Example/Json/Github/Workflows/Ci::workflow
- Generate YAML output for the same value (optional):
./bosatsuj lib json write --name core_alpha --main Bosatsu/Example/Json/Github/Workflows/Ci::workflow --yaml
Command reference
./bosatsuj lib json write [--repo_root <path>] [--name <lib_name>] --main <valueIdent> [--yaml] [--output <path>]
Common flags:
--name: library name from yourbosatsu_libs.json--main: package/value to evaluate and encode--yaml: emit YAML output (default remains JSON)--output: write JSON/YAML to a file instead of stdout
When --yaml is not set, lib json still emits JSON exactly as before.
Encoding shape
lib json uses type-directed encoding:
Int-> JSON numberFloat64-> JSON numberString-> JSON stringBool-> JSON booleanList[a]-> JSON arrayDict[String, a]-> JSON objectstruct-> JSON objectenum-> JSON object (constructors must remain distinguishable after encoding)
Option behavior
Option is especially useful for configuration records:
Noneencodes asnullwhen that is unambiguousSome(x)encodes asx- Nested options (
Option[Option[a]]) use array encoding to avoid ambiguity: - outer
None->[] - outer
Some(None)->[null] - outer
Some(Some(x))->[x]
For record fields, Option lets you model shape variation directly:
struct Step(
uses: Option[String],
run: Option[String],
with: Option[WithConfig],
)
This usually replaces many near-duplicate structs.
You can combine this with constructor defaults to remove repetitive None noise when building large JSON objects:
struct Step(
uses: Option[String] = None,
name: Option[String] = None,
run: Option[String] = None,
env: Option[StepEnv] = None,
)
checkout = Step { uses: Some("actions/checkout@v4") }
run_tests = Step { name: Some("run tests"), run: Some("sbt test") }
This is the default-arguments pattern for JSON records with many optional fields: define one struct with optional fields defaulted to None, then construct values with record syntax and set only the fields you need.
This pattern is especially useful for JSON-heavy models (like CI workflows) where many fields are optional most of the time. It keeps definitions compact, reduces copy/paste variants, and makes it safer to add new optional fields later without rewriting every call site.
Patterns that worked well for GitHub workflow modeling
- Put shared types/constants/helpers in a common module (for example
Util.bosatsu) - Use constants for repeated literals (runner names, action versions,
17, step labels) - Use helper functions for multi-line shell scripts (for example
cat_lines) - Prefer one record with
Option[...]fields over many one-off variants - Add
= Nonedefaults on frequently-optional fields, then construct with record syntax (Struct { field: Some(x) }) to keep values concise - Use backticks only when a field is not a valid bindable name, such as
`runs-on`and`timeout-minutes`
Practical gotchas
- Keep running
lib checkwhile editing models; it catches most mistakes before JSON generation. - JSON field names come from your Bosatsu field names exactly.
Float64JSON encoding requires finite values;.NaN,∞, and-∞are not valid JSON numbers.- If your consumer treats absent and
nulldifferently, model that intentionally and test it. lib json writerequires a serializable value. Function-typed mains are rejected.
Full commands used in this workflow example
These are the exact commands used to generate the four workflow values modeled in this example:
./bosatsuj lib json write --name core_alpha --main Bosatsu/Example/Json/Github/Workflows/Ci::workflow
./bosatsuj lib json write --name core_alpha --main Bosatsu/Example/Json/Github/Workflows/CodecovMain::workflow
./bosatsuj lib json write --name core_alpha --main Bosatsu/Example/Json/Github/Workflows/DeployWeb::workflow
./bosatsuj lib json write --name core_alpha --main Bosatsu/Example/Json/Github/Workflows/Release::workflow