json
stableParse, serialize, query, merge, diff, validate, flatten, and unflatten JSON data.
use plugin json::{parse, stringify, stringify_pretty, …} 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
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('{"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('{"theme": "light", "font": 12}')
let incoming = parse('{"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('{"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('{"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('{"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('{"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('{"a": 1, "b": 2}')
let v2 = parse('{"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('{"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('{"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('{"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))