Lcl · Core · Packages · Libraries


Json

namespace Json

lcl-json

JSON encoding and decoding for Lcl, backed by cJSON.

0.1.0 note: lcl-json is built and used in-tree only; it is not part of the cmake --install artifact. The package vendors cJSON via FetchContent, and a shipping decision for vendored dependencies is deferred. Use it by building Lcl with -DLCL_BUILD_JSON=ON and consuming lcl_json directly from your own CMake build.

Requirements

Build

cmake -S . -B build -DLCL_BUILD_JSON=ON
cmake --build build

Usage

;; Encode Lcl values to JSON text
let data #{name "Alice" age 30 active [Json::true]}
let text [Json::encode $data]      ;; {"name":"Alice","active":true,"age":30}

;; Decode JSON text to Lcl values
let parsed [Json::decode {{"items": [1, 2, 3], "valid": true}}]
let items [get $parsed items]      ;; (1 2 3)
if [Json::bool? [get $parsed valid]] { puts "valid is a JSON boolean" }

Type mapping

JSON Lcl
string string
number with an integral value int
number with a fractional part float
array list
object dict
true / false JSON boolean (opaque, see Json::true, Json::false)
null JSON null (opaque, see Json::null)

Going the other way, Json::encode maps Lcl strings, ints, floats, lists and dicts to their JSON counterparts and the opaque boolean and null values to true, false and null. Procs, namespaces and foreign opaque values cannot be encoded and raise an error.

Notes

Literal values

JSON’s true, false and null have no native Lcl counterpart, so the package represents them as opaque values. They print as <opaque:json_bool> and <opaque:json_null>, are not equal to the ints 1 and 0, and are only useful to Json::encode and the predicates Json::bool? and Json::null?.

proc Json::encode value

Encode an Lcl value as compact JSON text.

Strings, ints, floats, lists and dicts map to JSON strings, numbers, arrays and objects; the values made by Json::true, Json::false and Json::null map to the JSON literals. A string is always a JSON string, even when it looks numeric, because numbers are typed at scan time. Floats with an integral value print without a fraction. Procs, namespaces and other opaque values are an error.

Examples:

>> Json::encode 42
"42"
>> Json::encode "42"
"\"42\""
>> Json::encode 2.5
"2.5"
>> Json::encode 1.0
"1"
>> Json::encode (1 2.5 "x" (3 4) #{k v})
"\[1,2.5,\"x\",\[3,4],{\"k\":\"v\"}]"
>> Json::encode #{name "Alice"}
"{\"name\":\"Alice\"}"
>> Json::encode ()
"\[]"
>> Json::encode #{}
"{}"
>> Json::encode [Json::true]
"true"
>> Json::encode [Json::null]
"null"
>> Json::encode "a\nb"
"\"a\\nb\""
>> catch { Json::encode [lambda {x} { $x }] }
1

proc Json::decode text

Parse JSON text into an Lcl value.

Objects become dicts, arrays become lists, strings become strings, and numbers become ints when integral (and within long range) or floats otherwise. true, false and null become the opaque values recognised by Json::bool? and Json::null?. Malformed text is an error; anything after the first complete value is ignored. Decoding and re-encoding is stable.

Examples:

>> Json::decode {[1, 2.5, "x"]}
(1 2.5 "x")
>> Json::decode {{"a": {"b": [1, 2]}}}
#{"a" #{"b" (1 2)}}
>> Json::decode {"hi"}
"hi"
>> type [Json::decode {42}]
"int"
>> type [Json::decode {2.5}]
"float"
>> Json::decode {1e3}
1000
>> Json::bool? [get [Json::decode {{"ok": true}}] ok]
1
>> Json::null? [get [Json::decode {{"v": null}}] v]
1
>> Json::encode [Json::decode {{"x": [1, 2, {"y": null}]}}]
"{\"x\":\[1,2,{\"y\":null}]}"
>> catch { Json::decode {not json} }
1

proc Json::true

Return the JSON true value.

Examples:

>> Json::encode [Json::true]
"true"
>> Json::bool? [Json::true]
1
>> type [Json::true]
"opaque"

proc Json::false

Return the JSON false value.

Examples:

>> Json::encode [Json::false]
"false"
>> Json::encode #{flag [Json::false]}
"{\"flag\":false}"

proc Json::null

Return the JSON null value.

Examples:

>> Json::encode [Json::null]
"null"
>> Json::null? [Json::null]
1

proc Json::bool? value

Return 1 if value is a JSON boolean (from Json::true, Json::false or a decoded true/false), else 0.

Ints are never JSON booleans.

Examples:

>> Json::bool? [Json::true]
1
>> Json::bool? [Json::false]
1
>> Json::bool? 1
0
>> Json::bool? [Json::null]
0
>> List::map [Json::decode {[true, 0]}] [lambda {v} { Json::bool? $v }]
(1 0)

proc Json::null? value

Return 1 if value is the JSON null (from Json::null or a decoded null), else 0.

The string "null" and the empty string are not JSON null.

Examples:

>> Json::null? [Json::null]
1
>> Json::null? "null"
0
>> Json::null? ""
0
>> Json::null? [Json::true]
0