Skip to content

json

stable

Parse, serialize, query, merge, diff, validate, flatten, and unflatten JSON data.

use plugin json::{parse, stringify, stringify_pretty, …}
12 functions Data Formats
/ filter jk navigate Esc clear
Functions (12)
  1. parse Parse a JSON string into a value
  2. stringify Serialize a value to a JSON string
  3. stringify_pretty Serialize with indentation
  4. parse_file Parse JSON from a file path
  5. write_file Write a value as pretty JSON to a file
  6. get_path Read a nested value by dot-path
  7. set_path Write a nested value by dot-path
  8. merge Deep-merge two tables
  9. diff Compute added/removed/changed between two values
  10. validate_schema Validate data against a simple schema
  11. flatten Flatten nested tables to dot-keyed pairs
  12. 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))
enespt-br