json
stableParse, serialize, query, merge, diff, validate, flatten, and unflatten JSON data.
use plugin json Plugin snippets can be edited in the Playground, but native capabilities are executed locally with the Zolo CLI.
Functions (12)
- parse Parse a JSON string into a value
- stringify Serialize a value to a JSON string
- stringify_pretty Serialize with indentation
- parse_file Parse JSON from a file path
- write_file Write a value as pretty JSON to a file
- get_path Read a nested value by dot-path
- set_path Write a nested value by dot-path
- merge Deep-merge two tables
- diff Compute added/removed/changed between two values
- validate_schema Validate data against a simple schema
- flatten Flatten nested tables to dot-keyed pairs
- unflatten Restore dot-keyed pairs to nested tables
From import to result
- 01Import only what you use
- 02Copy the smallest example
- 03Run it with the Zolo CLI
Overview¶
json bridges JSON text and ordinary Zolo values. Parsing turns JSON into plain
tables — objects become string-keyed tables and arrays become 1-indexed
integer-keyed tables — so there is no opaque document type to learn: a parsed
document is just a table you index, iterate, and pass around. Serialization runs
the same mapping in reverse, treating a table whose keys are all integers as a
JSON array and any other table as an object.
Beyond parse/stringify, the plugin adds a small toolkit for working with that
data: dot-path read/write (get_path, set_path), deep merge and diff,
lightweight schema validation, and flatten/unflatten for collapsing nested
structures into single-level dot-keyed tables. The path-based and transform
helpers never mutate their input — they return new tables — so they compose
cleanly. Reach for json whenever you load configuration, exchange data with an
HTTP API, or need to compare or reshape structured values.
Common patterns¶
Load a config file, apply runtime overrides, and read a nested value back out:
use plugin json::{parse_file, merge, get_path}
let base = parse_file("config.json")
let config = merge(base, #{"server": #{"port": 8080}})
print("port: {get_path(config, "server.port")}")Validate an incoming payload against a schema before using it:
use plugin json::{parse, validate_schema}
let schema = #{
"type": "object",
"required": ["name", "age"],
"properties": #{
"name": #{"type": "string"},
"age": #{"type": "number"}
}
}
let data = parse(r#"{"name": "Alice", "age": 30}"#)
let report = validate_schema(data, schema)
if report["valid"] {
print("ok: {get_path(data, "name")}")
} else {
print("first error: {report["errors"][1]}")
}Compare two documents and persist the merged result:
use plugin json::{parse, diff, merge, write_file}
let current = parse(r#"{"theme": "light", "font": 12}"#)
let incoming = parse(r#"{"theme": "dark", "font": 12}"#)
let changes = diff(current, incoming)
print("{changes[1]["path"]} {changes[1]["type"]}")
write_file("settings.json", merge(current, incoming))Parse a JSON string into a value
Parses a JSON string and returns the equivalent Zolo value. Objects become string-keyed tables; arrays become 1-indexed integer-keyed tables.
use plugin json::{parse}
let data = parse(r#"{"name": "Alice", "scores": [10, 20, 30]}"#)
print(data["name"])
print(data["scores"][1])Because the result is an ordinary table, parsed values flow straight into the other helpers:
use plugin json::{parse, get_path}
let payload = parse(r#"{"user": {"roles": ["admin", "editor"]}}"#)
print(get_path(payload, "user.roles.0"))Serialize a value to a JSON string
Serializes a Zolo value to a compact JSON string. Integer-keyed tables become JSON arrays; string-keyed tables become JSON objects.
use plugin json::{stringify}
let text = stringify(#{"x": 1, "y": 2, "tags": ["a", "b"]})
print(text)A table whose keys are all integers serializes as a JSON array, so lists round-trip without extra wrapping:
use plugin json::{stringify}
print(stringify([1, 2, 3]))Serialize with indentation
Serializes a value to a human-readable JSON string. indent defaults to 2 spaces.
use plugin json::{stringify_pretty}
let obj = #{"name": "Bob", "active": true}
print(stringify_pretty(obj))
print(stringify_pretty(obj, 4))Parse JSON from a file path
Reads a file from disk and parses its contents as JSON.
use plugin json::{parse_file}
let config = parse_file("config.json")
print(config["host"])
print(config["port"])Write a value as pretty JSON to a file
Serializes value as pretty-printed JSON and writes it to the file at path, overwriting any existing content.
use plugin json::{write_file}
let settings = #{"theme": "dark", "font_size": 14}
write_file("settings.json", settings)Read a nested value by dot-path
Reads a nested value using a dot-separated path string. Array indices are 0-based in the path (e.g. "users.0.name").
use plugin json::{parse, get_path}
let data = parse(r#"{"users": [{"name": "Alice"}, {"name": "Bob"}]}"#)
let name = get_path(data, "users.0.name")
print(name)Write a nested value by dot-path
Returns a new table with the value at the dot-path set to value. Creates intermediate tables as needed. The original table is not mutated.
use plugin json::{parse, set_path, stringify}
let data = parse(r#"{"user": {"name": "Alice"}}"#)
let updated = set_path(data, "user.name", "Bob")
print(stringify(updated))Paths that do not exist yet are created on the way down, so set_path doubles as
a builder for deep structures:
use plugin json::{set_path, stringify}
let cfg = set_path(#{}, "server.tls.enabled", true)
print(stringify(cfg))Deep-merge two tables
Deep-merges table2 into table1. Keys in table2 overwrite matching keys in table1; nested tables are merged recursively.
use plugin json::{merge, stringify}
let base = #{"host": "localhost", "port": 3000, "debug": false}
let overrides = #{"port": 8080, "debug": true}
let config = merge(base, overrides)
print(stringify(config))Compute added/removed/changed between two values
Returns a list of changes between two values. Each entry is a table with path, type ("added", "removed", or "changed"), and old/new values where applicable.
use plugin json::{parse, diff}
let v1 = parse(r#"{"a": 1, "b": 2}"#)
let v2 = parse(r#"{"a": 1, "b": 3, "c": 4}"#)
let changes = diff(v1, v2)
print(changes[1]["path"])
print(changes[1]["type"])Validate data against a simple schema
Validates data against a simple schema table and returns {valid, errors}. The schema supports type ("object", "array", "string", "number", "boolean"), required (list of field names), and properties (nested schemas).
use plugin json::{parse, validate_schema}
let schema = #{
"type": "object",
"required": ["name", "age"],
"properties": #{
"name": #{"type": "string"},
"age": #{"type": "number"}
}
}
let data = parse(r#"{"name": "Alice", "age": 30}"#)
let result = validate_schema(data, schema)
print(result["valid"])Flatten nested tables to dot-keyed pairs
Flattens a nested table into a single-level table whose keys are dot-separated paths. The default separator is ".".
use plugin json::{parse, flatten}
let data = parse(r#"{"a": {"b": 1, "c": {"d": 2}}}"#)
let flat = flatten(data)
print(flat["a.b"])
print(flat["a.c.d"])Array elements flatten with 0-based index segments, and a custom separator is supported:
use plugin json::{parse, flatten}
let data = parse(r#"{"tags": ["x", "y"]}"#)
let flat = flatten(data, "/")
print(flat["tags/0"])
print(flat["tags/1"])Restore dot-keyed pairs to nested tables
Reconstructs a nested table from a flat dot-keyed table. Inverse of flatten.
use plugin json::{unflatten, stringify}
let flat = #{"a.b": 1, "a.c.d": 2}
let nested = unflatten(flat)
print(stringify(nested))