JsonLib — JSON Library

Comprehensive JSON manipulation for Plan9Basic. Parse, create, modify, and serialize JSON data structures for web APIs, configuration files, and data interchange. 47 functions with 61 registered signatures including overloads.

CategoryCountDescription
Creation7json_object#, json_array#, json_parse#, json_null#, json_bool#, json_number#, json_string#
Serialization2json_stringify$, json_pretty$ (with indent overload)
Type Checking8json_type, json_typename$, json_isobj/arr/str/num/bool/null
Object Access5json_get#, json_getn, json_gets$, json_getb, json_has (with default overloads)
Object Modification9json_set#, json_setn#, json_sets#, json_setb#, json_setnull#, json_remove#, json_keys#, json_count, json_merge#
Array Functions13json_len, json_item#/n/s$/b, json_push#/n/s/b/null, json_pop#, json_removeat#
Path Navigation4json_path#, json_pathn, json_paths$, json_pathb (with default overloads)
Utility3json_clone#, json_value, json_value$
FeatureDescription
Naming Convention# suffix = returns pointer, $ = returns string, no suffix = returns number
MemoryAll JSON values are garbage-collected automatically
Fluent APIModification functions return the object for chaining
Path NavigationDot notation + array indices: user.scores[0]

JSON Type System

CodeNameDescription
0nullNull value
1objectKey-value pairs {...}
2arrayOrdered list [...]
3stringText value
4numberNumeric value
5booleantrue or false
╯ plan9basic
obj# = json_object#()
arr# = json_array#()
str# = json_string#("hello")

println json_type(obj#)       ' 1 (object)
println json_typename$(arr#)  ' "array"
println json_isstr(str#)      ' 1

Creation Functions

FunctionSignatureDescription
json_object#()json_object#@Create empty JSON object
json_array#()json_array#@Create empty JSON array
json_parse#(str$)json_parse#@$Parse JSON string (nil on failure)
json_null#()json_null#@Create JSON null value
json_bool#(n)json_bool#@nCreate boolean (0=false, non-zero=true)
json_number#(n)json_number#@nCreate JSON number
json_string#(str$)json_string#@$Create JSON string
╯ plan9basic
' Create object and populate
obj# = json_object#()
json_sets#(obj#, "name", "John")
json_setn#(obj#, "age", 30)
json_setb#(obj#, "active", 1)
json_set#(obj#, "data", json_null#())
println json_stringify$(obj#)
' {"name":"John","age":30,"active":true,"data":null}

' Create array
arr# = json_array#()
json_pushn#(arr#, 1)
json_pushn#(arr#, 2)
json_pushn#(arr#, 3)
println json_stringify$(arr#)   ' [1,2,3]

' Parse existing JSON
data# = json_parse#("{\"x\":10,\"y\":20}")
println json_getn(data#, "x")   ' 10
⚠ Warning: json_parse#() returns nil if parsing fails. Always check with isassigned() before using the result on untrusted input.

Serialization

FunctionSignatureDescription
json_stringify$(json#)json_stringify$@#Compact JSON string (no whitespace)
json_pretty$(json#)json_pretty$@#Formatted with 2-space indent
json_pretty$(json#, n)json_pretty$@#nFormatted with n-space indent (0–8)
╯ plan9basic
obj# = json_object#()
json_sets#(obj#, "name", "Test")
json_setn#(obj#, "value", 42)

println json_stringify$(obj#)
' {"name":"Test","value":42}

println json_pretty$(obj#)
' {
'   "name": "Test",
'   "value": 42
' }

println json_pretty$(obj#, 4)
' {
'     "name": "Test",
'     "value": 42
' }

Type Checking

FunctionSignatureDescription
json_type(json#)json_type@#Type code (0–5)
json_typename$(json#)json_typename$@#Type name string
json_isobj(json#)json_isobj@#Is object? (1/0)
json_isarr(json#)json_isarr@#Is array? (1/0)
json_isstr(json#)json_isstr@#Is string? (1/0)
json_isnum(json#)json_isnum@#Is number? (1/0)
json_isbool(json#)json_isbool@#Is boolean? (1/0)
json_isnull(json#)json_isnull@#Is null? (1/0, also true for nil pointer)
╯ plan9basic
data# = json_parse#("{\"name\":\"John\",\"address\":null}")

println json_isnull(json_get#(data#, "name"))      ' 0
println json_isnull(json_get#(data#, "address"))   ' 1
println json_isnull(json_get#(data#, "missing"))   ' 1 (key absent)

Object Access

FunctionSignatureDescription
json_get#(obj#, key$)json_get#@#$Get value as pointer (nil if missing)
json_getn(obj#, key$)json_getn@#$Get as number (0 if missing)
json_getn(obj#, key$, def)json_getn@#$nGet as number with default
json_gets$(obj#, key$)json_gets$@#$Get as string ("" if missing)
json_gets$(obj#, key$, def$)json_gets$@#$$Get as string with default
json_getb(obj#, key$)json_getb@#$Get as boolean (1=true/truthy, 0=false)
json_has(obj#, key$)json_has@#$Check if key exists (1/0)
╯ plan9basic
data# = json_parse#("{\"name\":\"John\",\"age\":30,\"active\":true}")

println json_gets$(data#, "name")           ' John
println json_getn(data#, "age")             ' 30
println json_getb(data#, "active")          ' 1
println json_getn(data#, "missing", -1)     ' -1 (default)
println json_gets$(data#, "missing", "N/A") ' N/A

if json_has(data#, "email") = 1 then
    println json_gets$(data#, "email")
else
    println "No email"
endif
ⓘ Note: Use default-value overloads (json_getn(obj#, key$, default)) to simplify code and avoid separate existence checks.

Object Modification

FunctionSignatureDescription
json_set#(obj#, key$, val#)json_set#@#$#Set pointer value (deep-cloned)
json_setn#(obj#, key$, n)json_setn#@#$nSet number
json_sets#(obj#, key$, str$)json_sets#@#$$Set string
json_setb#(obj#, key$, n)json_setb#@#$nSet boolean (0=false)
json_setnull#(obj#, key$)json_setnull#@#$Set null
json_remove#(obj#, key$)json_remove#@#$Remove key
json_keys#(obj#)json_keys#@#Get all keys as JSON array
json_count(obj#)json_count@#Number of key-value pairs
json_merge#(target#, source#)json_merge#@##Merge source into target (overwrites)
╯ plan9basic
obj# = json_object#()
json_sets#(obj#, "firstName", "John")
json_setn#(obj#, "age", 35)
json_setb#(obj#, "active", 1)
json_setnull#(obj#, "middleName")
println json_stringify$(obj#)

' Add nested object
addr# = json_object#()
json_sets#(addr#, "city", "New York")
json_set#(obj#, "address", addr#)

' Remove a key
json_remove#(obj#, "middleName")

' Iterate keys
keys# = json_keys#(obj#)
for i = 0 to json_len(keys#) - 1
    println json_items$(keys#, i)
next

' Merge objects
defaults# = json_parse#("{\"color\":\"blue\",\"size\":10}")
custom# = json_parse#("{\"color\":\"red\",\"opacity\":0.5}")
json_merge#(defaults#, custom#)
println json_pretty$(defaults#)
ⓘ Note: All set functions return the object pointer for chaining. Values set with json_set#() are deep-cloned to avoid ownership issues.

Array Functions

FunctionSignatureDescription
json_len(json#)json_len@#Length of array (or object key count, or string length)
json_item#(arr#, idx)json_item#@#nGet item as pointer (0-based, nil if out of bounds)
json_itemn(arr#, idx)json_itemn@#nGet item as number
json_itemn(arr#, idx, def)json_itemn@#nnGet item as number with default
json_items$(arr#, idx)json_items$@#nGet item as string
json_items$(arr#, idx, def$)json_items$@#n$Get item as string with default
json_itemb(arr#, idx)json_itemb@#nGet item as boolean
json_push#(arr#, val#)json_push#@##Push pointer value (deep-cloned)
json_pushn#(arr#, n)json_pushn#@#nPush number
json_pushs#(arr#, str$)json_pushs#@#$Push string
json_pushb#(arr#, n)json_pushb#@#nPush boolean
json_pushnull#(arr#)json_pushnull#@#Push null
json_pop#(arr#)json_pop#@#Pop and return last item (nil if empty)
json_removeat#(arr#, idx)json_removeat#@#nRemove item at 0-based index
╯ plan9basic
' Build array
scores# = json_array#()
json_pushn#(scores#, 85)
json_pushn#(scores#, 92)
json_pushn#(scores#, 78)
json_pushn#(scores#, 95)
println json_stringify$(scores#)  ' [85,92,78,95]
println "Count: "; json_len(scores#) ' 4

' Access items (0-based!)
println json_itemn(scores#, 0)       ' 85
println json_itemn(scores#, 99, -1)  ' -1 (default)

' String array
fruits# = json_array#()
json_pushs#(fruits#, "apple")
json_pushs#(fruits#, "banana")
println json_items$(fruits#, 0)      ' apple

' Pop and remove
item# = json_pop#(scores#)
println json_value(item#)             ' 95
json_removeat#(scores#, 1)            ' remove 92
println json_stringify$(scores#)      ' [85,78]
⚠ Warning: JSON array indices are 0-based, unlike Plan9Basic arrays which are 1-based.

Path Navigation

Access deeply nested values using dot notation and array indices.

PatternDescriptionExample
keyObject key"name"
key.subkeyNested key"user.profile"
[n]Array index"[0]"
key[n]Key then index"items[0]"
key[n].propCombined"users[0].name"
FunctionSignatureDescription
json_path#(json#, path$)json_path#@#$Navigate to pointer (nil if not found)
json_pathn(json#, path$)json_pathn@#$Get number at path
json_pathn(json#, path$, def)json_pathn@#$nGet number at path with default
json_paths$(json#, path$)json_paths$@#$Get string at path
json_paths$(json#, path$, def$)json_paths$@#$$Get string at path with default
json_pathb(json#, path$)json_pathb@#$Get boolean at path
╯ plan9basic
data# = json_parse#("{\"config\":{\"width\":800,\"height\":600}}")
println json_pathn(data#, "config.width")       ' 800
println json_pathn(data#, "config.depth", 100)  ' 100 (default)

data2# = json_parse#("{\"users\":[{\"name\":\"Alice\"},{\"name\":\"Bob\"}]}")
println json_paths$(data2#, "users[0].name")    ' Alice
println json_paths$(data2#, "users[1].name")    ' Bob

settings# = json_parse#("{\"notifications\":{\"email\":true,\"sms\":false}}")
println json_pathb(settings#, "notifications.email")  ' 1
println json_pathb(settings#, "notifications.sms")    ' 0
ⓘ Note: Use path navigation for deeply nested data. For shallow objects, direct access with json_gets$() etc. is simpler.

Utility Functions

FunctionSignatureDescription
json_clone#(json#)json_clone#@#Deep copy of any JSON value
json_value(json#)json_value@#Numeric value of primitive (bool true→1, false→0)
json_value$(json#)json_value$@#String value of primitive (null→"")
╯ plan9basic
original# = json_parse#("{\"name\":\"John\",\"scores\":[1,2,3]}")
copy# = json_clone#(original#)
json_sets#(copy#, "name", "Jane")

println json_gets$(original#, "name")  ' John (unchanged)
println json_gets$(copy#, "name")      ' Jane

num# = json_number#(42)
bool# = json_bool#(1)
println json_value(num#)     ' 42
println json_value(bool#)    ' 1
println json_value$(num#)    ' 42

Complete Examples

Building JSON from Scratch

╯ build_json.bas
' Create a person object with nested data
person# = json_object#()
json_sets#(person#, "firstName", "John")
json_sets#(person#, "lastName", "Doe")
json_setn#(person#, "age", 35)
json_setb#(person#, "employed", 1)

address# = json_object#()
json_sets#(address#, "street", "123 Main St")
json_sets#(address#, "city", "New York")
json_sets#(address#, "zip", "10001")
json_set#(person#, "address", address#)

hobbies# = json_array#()
json_pushs#(hobbies#, "reading")
json_pushs#(hobbies#, "gaming")
json_pushs#(hobbies#, "hiking")
json_set#(person#, "hobbies", hobbies#)

println json_pretty$(person#)

Parsing and Querying

╯ parse_query.bas
' Parse JSON and extract with paths
json$ = "{\"company\":\"Acme Inc\",\"employees\":[{\"name\":\"Alice\",\"dept\":\"Engineering\"},{\"name\":\"Bob\",\"dept\":\"Sales\"}]}"
data# = json_parse#(json$)

println "Company: "; json_gets$(data#, "company")

empCount = json_len(json_get#(data#, "employees"))
for i = 0 to empCount - 1
    path$ = "employees[" + stri$(i, 0) + "]"
    println "  "; json_paths$(data#, path$ + ".name"); " - "; json_paths$(data#, path$ + ".dept")
next

Modifying Existing JSON

╯ modify_json.bas
' Load, modify config
config# = json_parse#("{\"version\":1,\"debug\":false,\"maxConn\":10}")

println "Before:"
println json_pretty$(config#)

json_setn#(config#, "version", 2)
json_setb#(config#, "debug", 1)
json_setn#(config#, "maxConn", 50)
json_sets#(config#, "server", "localhost")

println "After:"
println json_pretty$(config#)

Array Score Calculator

╯ scores.bas
' Array manipulation
scores# = json_array#()
json_pushn#(scores#, 85)
json_pushn#(scores#, 92)
json_pushn#(scores#, 78)
json_pushn#(scores#, 95)
json_pushn#(scores#, 88)

println "Scores: "; json_stringify$(scores#)
println "Count: "; json_len(scores#)

total = 0
for i = 0 to json_len(scores#) - 1
    total = total + json_itemn(scores#, i)
next
println "Average: "; total / json_len(scores#)

json_removeat#(scores#, 2)
println "After removing 78: "; json_stringify$(scores#)

Object Key Iteration

╯ iterate.bas
' Iterate through all keys
data# = json_parse#("{\"name\":\"Product A\",\"price\":29.99,\"stock\":150,\"active\":true}")

keys# = json_keys#(data#)
println "Properties: "; json_len(keys#)

for i = 0 to json_len(keys#) - 1
    key$ = json_items$(keys#, i)
    value# = json_get#(data#, key$)
    typeName$ = json_typename$(value#)
    
    print "  "; key$; " ("; typeName$; "): "
    if typeName$ = "string" then
        println json_gets$(data#, key$)
    else if typeName$ = "number" then
        println json_getn(data#, key$)
    else if typeName$ = "boolean" then
        if json_getb(data#, key$) = 1 then
            println "true"
        else
            println "false"
        endif
    else
        println json_stringify$(value#)
    endif
next

Best Practices

Null Safety

╯ plan9basic
' Always check parse results on untrusted input
data# = json_parse#(userInput$)
if isassigned(data#) = 1 then
    ' Safe to use
else
    println "Invalid JSON"
endif

Default Values

╯ plan9basic
' Use defaults instead of existence checks
timeout = json_getn(config#, "timeout", 30)
server$ = json_gets$(config#, "server", "localhost")

Building Complex Structures

╯ plan9basic
' Build incrementally for readability
root# = json_object#()

profile# = json_object#()
json_sets#(profile#, "bio", "Developer")
json_set#(root#, "profile", profile#)

tags# = json_array#()
json_pushs#(tags#, "coding")
json_pushs#(tags#, "music")
json_set#(root#, "tags", tags#)

Quick Reference

FunctionSignatureDescription
json_object#() / json_array#()*#@Create empty object / array
json_parse#(str$)json_parse#@$Parse JSON string
json_null#() / json_bool#(n)*#@[n]Create null / boolean
json_number#(n) / json_string#(s$)*#@n/*#@$Create number / string
json_stringify$(j#)json_stringify$@#Compact JSON string
json_pretty$(j#[, n])json_pretty$@#[n]Formatted JSON string
json_type(j#) / json_typename$(j#)*@#Type code / name
json_isobj/arr/str/num/bool/null(j#)json_is*@#Type checks (1/0)
json_get#(o#, k$)json_get#@#$Get value pointer
json_getn(o#, k$[, def])json_getn@#$[n]Get number [with default]
json_gets$(o#, k$[, def$])json_gets$@#$[$]Get string [with default]
json_getb(o#, k$) / json_has(o#, k$)*@#$Get boolean / check key
json_set#/setn#/sets#/setb#/setnull#json_set*@#$*Set object values
json_remove#(o#, k$)json_remove#@#$Remove key
json_keys#(o#) / json_count(o#)*@#Keys array / key count
json_merge#(tgt#, src#)json_merge#@##Merge objects
json_len(j#)json_len@#Array/object/string length
json_item#/n/s$/b(a#, idx[, def])json_item*@#n[*]Get array item
json_push#/n/s/b/null#(a#, val)json_push*@#*Push to array
json_pop#(a#) / json_removeat#(a#, idx)*@#[n]Pop / remove at index
json_path#/n/s$/b(j#, path$[, def])json_path*@#$[*]Path navigation
json_clone#(j#)json_clone#@#Deep copy
json_value(j#) / json_value$(j#)json_value*@#Primitive value

47 functions (61 registered signatures with overloads) across 8 categories.