- Compiler: PP → Lexer → Parser → Analyzer → Gengo pipeline - Parser: 232/236 (98%) Harbour compatibility, registry-based dispatch - RTL: 351 Harbour-compatible functions - RDD: DBF/NTX/CDX engines with Rushmore bitmap optimization - Go Interop: IMPORT + pkg.Func() + obj:Method() with FastPath (15M calls/sec) - HB_FUNC API: Full Harbour C API compatible Go bridge - Concurrency: SPAWN/LAUNCH/GOROUTINE, <-, WATCH, PARALLEL FOR, ASYNC/AWAIT - Extensions: Multi-return, DEFER, Slice, f-string, Nil-safe ?:, CONST - Macro Compiler: Runtime AST parsing and evaluation - Debugger: TUI debugger with source display, breakpoints, stepping - FRB: Native + Pcode dual mode runtime binary - Tests: 13 packages ALL PASS Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
9.0 KiB
Five JSON — Harbour Compatible + Go-Native Extensions
Go's
encoding/json+net/httppower in Harbour syntax
Copyright (c) 2026 Charles KWON OhJun (charleskwonohjun@gmail.com). All rights reserved.
Overview
Five provides full Harbour JSON compatibility (hb_jsonEncode/hb_jsonDecode)
plus nine Go-native extension functions that go far beyond what Harbour can do.
These extensions leverage Go's standard library for JSONPath queries, HTTP
integration, file I/O, validation, and deep merge — all impossible in stock
Harbour without external C libraries.
Why Five's JSON Is Better
| Capability | Harbour | Five |
|---|---|---|
| Basic encode/decode | hb_jsonEncode() / hb_jsonDecode() |
Same API, compatible |
| Pretty print | Not available | JsonPretty(xValue) |
| JSONPath query | Not available | JsonPath(xVal, "$.user.name") |
| Deep merge | Not available | JsonMerge(hDest, hSrc) |
| Validate syntax | Not available | JsonValid(cJSON) |
| Detect type | Not available | JsonType(cJSON) |
| File read/write | Manual MemoRead + encode/decode |
JsonTo() / JsonFrom() |
| HTTP GET + JSON | External library required | JsonHttpGet(cURL) |
| HTTP POST + JSON | External library required | JsonHttpPost(cURL, xBody) |
| Unicode support | Limited by codepage | Full UTF-8 via Go |
| Large file streaming | Memory-limited | Go's streaming decoder |
| Concurrent encoding | Thread-unsafe | Goroutine-safe |
What Harbour Cannot Do
// Harbour: requires hbcurl + manual JSON parsing + error handling
// Approximately 30+ lines of code with external dependencies
// Five: one line, zero dependencies
result := JsonHttpGet("https://api.github.com/repos/user/repo")
? JsonPath(result, "$.body")
Function Reference
Harbour Compatible
hb_jsonEncode(xValue [, lHumanReadable]) → cJSON
Converts any Five value to a JSON string.
? hb_jsonEncode({"name" => "Five", "version" => 1})
// → {"name":"Five","version":1}
? hb_jsonEncode({"a" => {1,2,3}}, .T.) // pretty
// → {
// "a": [1, 2, 3]
// }
Supported types:
- String →
"string" - Numeric (int) →
123 - Numeric (float) →
3.14 - Logical →
true/false - NIL →
null - Array →
[1, 2, 3] - Hash →
{"key": "value"} - Nested structures → fully recursive
hb_jsonDecode(cJSON) → xValue
Parses a JSON string into Five values.
result := hb_jsonDecode('{"users":[{"name":"Kim"},{"name":"Lee"}]}')
? result["users"][1]["name"] // → "Kim"
Type mapping:
"string"→ Five String123→ Five Int3.14→ Five Doubletrue/false→ Five Logicalnull→ Five NIL[...]→ Five Array{...}→ Five Hash
Five Extensions (Go-Native)
JsonPretty(xValue [, cIndent]) → cJSON
Formats JSON with indentation for human readability.
h := {"name" => "Five", "features" => {"goroutine", "FRB", "Rushmore"}}
? JsonPretty(h)
// {
// "name": "Five",
// "features": [
// "goroutine",
// "FRB",
// "Rushmore"
// ]
// }
? JsonPretty(h, "\t") // tab-indented
JsonPath(xValue, cPath) → xResult
Queries nested JSON structures using dot-notation path syntax.
data := hb_jsonDecode('{"user":{"name":"Charles","scores":[100,95,88]}}')
? JsonPath(data, "$.user.name") // → "Charles"
? JsonPath(data, "$.user.scores[0]") // → 100
? JsonPath(data, "$.user.scores[2]") // → 88
? JsonPath(data, "$.missing.key") // → NIL
Path syntax:
$.key— root-level key$.key.subkey— nested key$.array[0]— array index (0-based)$.key.array[1].name— mixed nesting
JsonMerge(hDest, hSrc) → hMerged
Deep merges two hashes. Source keys overwrite destination keys.
defaults := {"host" => "localhost", "port" => 5432, "ssl" => .F.}
override := {"port" => 3306, "ssl" => .T., "db" => "myapp"}
config := JsonMerge(defaults, override)
? hb_jsonEncode(config)
// → {"host":"localhost","port":3306,"ssl":true,"db":"myapp"}
JsonValid(cJSON) → lValid
Validates JSON syntax without decoding.
? JsonValid('{"name":"Five"}') // → .T.
? JsonValid('{broken json') // → .F.
? JsonValid('') // → .F.
Uses Go's json.Valid() — faster than full decode for validation-only checks.
JsonType(cJSON) → cType
Detects the top-level JSON type without decoding.
? JsonType('{"a":1}') // → "object"
? JsonType('[1,2,3]') // → "array"
? JsonType('"hello"') // → "string"
? JsonType('42') // → "number"
? JsonType('true') // → "boolean"
? JsonType('null') // → "null"
? JsonType('{bad') // → "invalid"
JsonTo(xValue, cFile) → lSuccess
Writes a value as formatted JSON to a file.
config := {"host" => "db.example.com", "port" => 5432}
JsonTo(config, "config.json")
// File contents:
// {
// "host": "db.example.com",
// "port": 5432
// }
JsonFrom(cFile) → xValue
Reads and parses a JSON file.
config := JsonFrom("config.json")
? config["host"] // → "db.example.com"
? config["port"] // → 5432
JsonHttpGet(cURL [, nTimeout]) → hResult
Performs an HTTP GET request and returns the result as a hash.
result := JsonHttpGet("https://api.github.com/repos/user/repo")
? result["status"] // → 200
? result["error"] // → "" (empty if no error)
// Parse JSON body
data := hb_jsonDecode(result["body"])
? JsonPath(data, "$.full_name") // → "user/repo"
Result hash:
status— HTTP status code (200, 404, etc.)body— response body as stringerror— error message (empty if success)
Timeout: Default 30 seconds. Override with second parameter.
JsonHttpPost(cURL, xBody [, nTimeout]) → hResult
Performs an HTTP POST with JSON body.
// Post a hash — automatically serialized to JSON
result := JsonHttpPost("https://api.example.com/users", ;
{"name" => "Charles", "email" => "charles@example.com"})
? result["status"] // → 201
// Post raw JSON string
result := JsonHttpPost("https://api.example.com/data", ;
'{"raw":"json string"}')
Content-Type: Automatically set to application/json.
Use Cases
REST API Client
// Complete REST API client in Five — impossible in stock Harbour
// GET
users := hb_jsonDecode(JsonHttpGet("https://api.example.com/users")["body"])
FOR EACH user IN users
? JsonPath(user, "$.name"), JsonPath(user, "$.email")
NEXT
// POST
result := JsonHttpPost("https://api.example.com/users", ;
{"name" => "New User", "role" => "admin"})
IF result["status"] = 201
? "User created!"
ENDIF
Configuration File
// Load config with defaults + override
defaults := JsonFrom("defaults.json")
local_config := JsonFrom("local.json")
config := JsonMerge(defaults, local_config)
? "Database:", JsonPath(config, "$.database.host")
Data Validation
cInput := GetUserInput()
IF !JsonValid(cInput)
? "Invalid JSON!"
RETURN
ENDIF
IF JsonType(cInput) != "object"
? "Expected JSON object!"
RETURN
ENDIF
data := hb_jsonDecode(cInput)
Database Export to JSON
USE "customers"
LOCAL aRecords := {}
GO TOP
DO WHILE !Eof()
AAdd(aRecords, {"id" => FieldGet(1), "name" => AllTrim(FieldGet(2))})
SKIP
ENDDO
JsonTo(aRecords, "customers.json")
? "Exported", Len(aRecords), "records"
Goroutine + JSON API (Five exclusive)
// Parallel API calls — impossible in Harbour
ch := Channel(3)
Go({|c| ChSend(c, JsonHttpGet("https://api1.example.com/data"))}, ch)
Go({|c| ChSend(c, JsonHttpGet("https://api2.example.com/data"))}, ch)
Go({|c| ChSend(c, JsonHttpGet("https://api3.example.com/data"))}, ch)
// Collect results
FOR i := 1 TO 3
result := ChReceive(ch)
? "API", i, "status:", result["status"]
NEXT
Verified Test Results
=== Five JSON (Go-native extensions) ===
1. hb_jsonEncode:
{"features":["goroutine","FRB","Rushmore"],"name":"Five","version":1}
2. JsonPretty:
{
"features": ["goroutine","FRB","Rushmore"],
"name": "Five",
"version": 1
}
3. JsonPath:
$.user.name: Charles
$.user.scores[1]: 95
4. JsonMerge:
{"x":1,"y":99,"z":3}
5. JsonValid:
{"ok":true} → .T.
{broken → .F.
6. JsonType:
{"a":1} → object
[1,2,3] → array
"hello" → string
42 → number
7. JsonTo/JsonFrom:
Loaded name: Five
Migration from Harbour
| Harbour | Five | Notes |
|---|---|---|
hb_jsonEncode(x) |
hb_jsonEncode(x) |
100% compatible |
hb_jsonDecode(s) |
hb_jsonDecode(s) |
100% compatible |
hb_jsonEncode(x) + manual indent |
JsonPretty(x) |
One function |
MemoWrit(f, hb_jsonEncode(x)) |
JsonTo(x, f) |
One function |
hb_jsonDecode(MemoRead(f)) |
JsonFrom(f) |
One function |
| Not possible | JsonPath(x, "$.a.b[0]") |
Five exclusive |
| Not possible | JsonMerge(h1, h2) |
Five exclusive |
| Not possible | JsonHttpGet(url) |
Five exclusive |
| Not possible | JsonHttpPost(url, body) |
Five exclusive |
| hbcurl + manual parsing | JsonHttpGet() |
Zero dependencies |