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.
| Category | Count | Description |
|---|---|---|
| Creation | 7 | json_object#, json_array#, json_parse#, json_null#, json_bool#, json_number#, json_string# |
| Serialization | 2 | json_stringify$, json_pretty$ (with indent overload) |
| Type Checking | 8 | json_type, json_typename$, json_isobj/arr/str/num/bool/null |
| Object Access | 5 | json_get#, json_getn, json_gets$, json_getb, json_has (with default overloads) |
| Object Modification | 9 | json_set#, json_setn#, json_sets#, json_setb#, json_setnull#, json_remove#, json_keys#, json_count, json_merge# |
| Array Functions | 13 | json_len, json_item#/n/s$/b, json_push#/n/s/b/null, json_pop#, json_removeat# |
| Path Navigation | 4 | json_path#, json_pathn, json_paths$, json_pathb (with default overloads) |
| Utility | 3 | json_clone#, json_value, json_value$ |
| Feature | Description |
|---|---|
| Naming Convention | # suffix = returns pointer, $ = returns string, no suffix = returns number |
| Memory | All JSON values are garbage-collected automatically |
| Fluent API | Modification functions return the object for chaining |
| Path Navigation | Dot notation + array indices: user.scores[0] |
JSON Type System
| Code | Name | Description |
|---|---|---|
| 0 | null | Null value |
| 1 | object | Key-value pairs {...} |
| 2 | array | Ordered list [...] |
| 3 | string | Text value |
| 4 | number | Numeric value |
| 5 | boolean | true or false |
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
| Function | Signature | Description |
|---|---|---|
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#@n | Create boolean (0=false, non-zero=true) |
json_number#(n) | json_number#@n | Create JSON number |
json_string#(str$) | json_string#@$ | Create JSON string |
' 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
| Function | Signature | Description |
|---|---|---|
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$@#n | Formatted with n-space indent (0–8) |
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
| Function | Signature | Description |
|---|---|---|
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) |
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
| Function | Signature | Description |
|---|---|---|
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@#$n | Get 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) |
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
| Function | Signature | Description |
|---|---|---|
json_set#(obj#, key$, val#) | json_set#@#$# | Set pointer value (deep-cloned) |
json_setn#(obj#, key$, n) | json_setn#@#$n | Set number |
json_sets#(obj#, key$, str$) | json_sets#@#$$ | Set string |
json_setb#(obj#, key$, n) | json_setb#@#$n | Set 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) |
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
| Function | Signature | Description |
|---|---|---|
json_len(json#) | json_len@# | Length of array (or object key count, or string length) |
json_item#(arr#, idx) | json_item#@#n | Get item as pointer (0-based, nil if out of bounds) |
json_itemn(arr#, idx) | json_itemn@#n | Get item as number |
json_itemn(arr#, idx, def) | json_itemn@#nn | Get item as number with default |
json_items$(arr#, idx) | json_items$@#n | Get item as string |
json_items$(arr#, idx, def$) | json_items$@#n$ | Get item as string with default |
json_itemb(arr#, idx) | json_itemb@#n | Get item as boolean |
json_push#(arr#, val#) | json_push#@## | Push pointer value (deep-cloned) |
json_pushn#(arr#, n) | json_pushn#@#n | Push number |
json_pushs#(arr#, str$) | json_pushs#@#$ | Push string |
json_pushb#(arr#, n) | json_pushb#@#n | Push 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#@#n | Remove item at 0-based index |
' 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.
| Pattern | Description | Example |
|---|---|---|
key | Object key | "name" |
key.subkey | Nested key | "user.profile" |
[n] | Array index | "[0]" |
key[n] | Key then index | "items[0]" |
key[n].prop | Combined | "users[0].name" |
| Function | Signature | Description |
|---|---|---|
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@#$n | Get 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 |
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
| Function | Signature | Description |
|---|---|---|
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→"") |
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
' 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 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
' 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
' 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 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
' 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
' Use defaults instead of existence checks timeout = json_getn(config#, "timeout", 30) server$ = json_gets$(config#, "server", "localhost")
Building Complex Structures
' 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
| Function | Signature | Description |
|---|---|---|
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.