PBToolboxAI v5 ← Site

json — n_pbt_json #

← Component reference · Guide contents

Build a JSON type by type — string, number, boolean, array, nested object — with the escaping done for you, then read one back along a path: of_load, of_get_string, of_type, of_count. A single object builds your requests and reads your responses. All in pure PowerScript: no page, no engine, PowerBuilder 10 included.

▶ See it live — Demo application, JSON tile: the results, the code behind them and this page, side by side.


At a glance #

n_pbt_json is autoinstantiate: declare it as a local variable, no create or destroy. It is the very object that internally builds the payloads sent to the engine — now offered to your code, on the build and the read side.

Quick start #

Build a payload, then read a response back — it all fits in one local variable:

// Local variables
n_pbt_json  lnv_json
string      ls_customer
double      ld_total

// Write : each member is typed and escaped for you - a path may have levels
lnv_json.of_set_string(/*path*/ "customer", /*value*/ "Ada")
lnv_json.of_set_number(/*path*/ "total", /*value*/ 690.50)
lnv_json.of_set_boolean(/*path*/ "paid", /*value*/ true)

// lnv_json.of_text() -> {"customer":"Ada","total":690.5,"paid":true}

// Read : load a response, reach values by path
lnv_json.of_load(/*json*/ '{"order":{"customer":"Ada","total":690.50}}')
IF lnv_json.of_is_valid() THEN
	ls_customer = lnv_json.of_get_string(/*path*/ "order/customer")   // Ada
	ld_total    = lnv_json.of_get_number(/*path*/ "order/total")     // 690.5
END IF

A path is the keys from the root joined by /; an array index is its position, counted from 1, like every position of the library: order/lines/1 is the first line, order/lines/0 addresses nothing — a write refuses it with -5. A level is written as JSON Pointer (RFC 6901) writes it: ~1 is a / of the key and ~0 a ~ (paths/~1users reads the key /users; of_escape_key converts a key), an empty level is the key "" (a//b), and a path may start with / — the full pointer form, the only way to name the key "" of the root (/). When an object writes the same key twice, its FIRST occurrence is the one read and written — a browser keeps the last one.


Build a payload #

MethodRole
of_add_string (string as_path, string as_value) → longAppends a STRING, escaped for you, to the ARRAY at as_path. The array must already be there: open it first with of_set_raw and an empty array. Returns 0, -4 when as_path is missing or is not an array, -5 when a level of as_path is an index 0 on an array; a null string is written empty (for a JSON null: of_add_raw(as_path, "null"))
of_add_boolean (string as_path, boolean ab_value) → longAppends a BOOLEAN (true or false, never quoted) to the ARRAY at as_path. The array must already be there: open it first with of_set_raw and an empty array. Returns 0, -4 when as_path is missing or is not an array, -5 when a level of as_path is an index 0 on an array; a null value writes null
of_add_number (string as_path, long al_value) → longAppends a NUMBER (five overloads: integer, long, longlong, decimal, double — a decimal and a longlong keep EVERY digit (the cents of an amount, the last digit of an id above 2^53), and a literal such as 690.50 is a decimal, written 690.5) to the ARRAY at as_path. The array must already be there: open it first with of_set_raw and an empty array. Returns 0, -4 when as_path is missing or is not an array, -5 when a level of as_path is an index 0 on an array; a null value writes null
of_add_raw (string as_path, string as_json) → longAppends a value ALREADY in JSON (an object, an array) you vouch for to the ARRAY at as_path. The array must already be there: open it first with of_set_raw and an empty array. Returns 0, -4 when as_path is missing or is not an array, -5 when a level of as_path is an index 0 on an array; a null or empty one writes null. The empty path is the ROOT: of_load("[]") then of_add_raw("", …) builds a document that is an array (the body of a bulk API call). Adding many elements in a row to the same array stays quick
of_set_array (string as_path, string as_values[]) → longWRITES a whole ARRAY of strings at as_path from a PowerBuilder array, each element escaped. An empty array writes an empty one, a null element an empty string. Same path rule and same codes as of_set_string
of_set_members (string as_fragment) → longMerges members already formed elsewhere at the ROOT, comma-joined. A key the root already holds has its value replaced in place, a new one is added at the end, a key the fragment repeats keeps its last value. An empty fragment writes nothing. Returns 0, -4 when the root of the document is not an object
of_clear ( ) → longEmpties the document to start another object with the same variable. Returns 0
of_text ( ) → stringThe document as it stands (compact): what you built (of_add_string, of_add_number...) or loaded (of_load), after any change — the single output, ready to send, store or save back. of_pretty is the indented form. {} when empty; calling it empties nothing

Read a document #

MethodRole
of_load (string as_json) → longLoads a JSON text for reading. Returns 0 when well-formed, -5 when the argument is empty or blank (spaces, tabs, line breaks), -4 when it is not valid JSON or nests more than 512 levels (the document is then dropped). Parses in pure PowerScript. A byte order mark (U+FEFF) in front, the one of a file saved as UTF-8 with BOM, is skipped. On a negative code, of_get_last_error says where the text is wrong (line, column) and of_error_position at which character. A document may be a single value ("abc", 42, null): it reads at the empty path, nothing is written into it
of_is_valid ( ) → booleanTrue when the object holds a document: the last of_load succeeded, or something was written since. A value given to of_set_raw / of_add_raw is not re-checked
of_exists (string as_path) → booleanTrue when a value lives at the given path — tells a present-but-empty value from an absent one
of_type (string as_path) → stringThe JSON type at the path: object, array, string, number, boolean, null; empty if absent
of_get_string (string as_path) → stringThe value at the path, as text: a string comes back decoded, a number/boolean/null as text, an object or array as raw JSON (reloadable by of_load); a key written twice is read at its FIRST occurrence
of_get_number (string as_path) → doubleThe number at the path, as a double — the decimal dot is honoured on any machine (2.5 is 2.5, not 2). 0 if absent; the exponent is resolved (1e-7, 2.5E+3) and 17 significant digits are kept, leading zeros of the exponent included. For an amount or an id that must keep every digit: of_get_decimal or of_get_longlong. A string counts only when all of it is a JSON number: "12.50" is 12.5, "12 kg" and "1,5" are 0
of_get_decimal (string as_path) → decimalThe number at the path, as a decimal, rebuilt from the TEXT of the number and never through a double: 12345678901234.56 comes back with its cents, 2.5E-3 is 0.0025. 28 significant digits at most. 0 if absent, when the value is not a number, or beyond what a decimal holds; a string counts only when all of it is a number ("12.50")
of_get_longlong (string as_path) → longlongThe number at the path, as a 64-bit integer, rebuilt from its text and never through a double: a database id such as 9007199254740993 comes back whole. Decimals are cut off (2.9 gives 2). 0 if absent, when the value is not a number, or beyond the range of a longlong; a string counts only when all of it is a number ("12.50")
of_get_boolean (string as_path) → booleanThe boolean at the path: true for the JSON literal true and for the string "true" some APIs write; false for anything else
of_count (string as_path) → longThe number of members of the object, or elements of the array, at the path. Returns -1 when the value is scalar or absent (tells an empty array, 0, from a missing one, -1). With of_name, it walks an object; an array is walked by its index in the path (rows/1, rows/2…). Called at every turn of a loop, it costs nothing once the array has been counted, removals included
of_name (string as_path, long al_index) → stringThe name of the al_index-th member (1-based) of the object at the path — to read the keys of an object whose shape is unknown. With of_count, it walks the object. To make it a path level: of_escape_key (a key may hold a /)
of_escape_key (string as_key) → stringA KEY written as ONE level of a path (JSON Pointer, RFC 6901): a ~ becomes ~0, then a / becomes ~1 — the key application/json is the level application~1json. What a key read by of_name needs before it goes into a path
of_get_array (string as_path, ref string as_values[]) → longEvery element of the array at the path, in order, in as_values (emptied first) — each one as of_get_string reads it: a string comes back decoded, a number or a boolean as text, null as "", an object or an array as raw JSON (to of_load in another n_pbt_json). "" is the root. Returns the number of elements (0 for an empty array), -4 when there is no array at the path (nothing, an object, a scalar), -5 when a level of the path is an index 0 on an array
of_pretty ( ) → stringThe document — loaded or built — re-indented for reading (several lines, tabs) — a log, a text box. Empty when there is none, or when a value given to of_set_raw nests more than 512 levels (the text would not be the document)
of_pretty (long al_indent) → stringThe document — loaded or built — re-indented with al_indent spaces per level (2 and 4 are the usual widths). 0 writes it compact, on one line and without a space: the smallest text to store or send. Empty when there is no document, or when al_indent is negative, above 16 or null, or when a value given to of_set_raw nests more than 512 levels. of_pretty() without an argument keeps the tabs
// Local variables
n_pbt_json  lnv_json
string      ls_tags[]
string      ls_compact
long        ll_count

// Read every element of an array at once, then store the document on one line
lnv_json.of_load(/*json*/ '{"customer":{"name":"Ada","tags":["vip","b2b"]}}')
ll_count = lnv_json.of_get_array(/*path*/ "customer/tags", /*values*/ ls_tags)   // 2 : vip, b2b

// The document on one line, without a space : the smallest text to store
ls_compact = lnv_json.of_pretty(/*indent*/ 0)

Modify a document #

The write side changes the loaded document in place, then of_text returns the result (compact) — what to save back. The setters replace the value at the path, or add the last key when it is missing from a parent object. Each returns 0, -4 when a level on the way is a scalar, or an array without that index, -5 on an empty path or an index 0 on an array. An array met on the way is crossed by an index that EXISTS (items/1/qty adds qty to the first element); only an object key is ever created. A null value (number, boolean, empty raw JSON) is written null. A loop of writes, of added keys or of removals in a big document (2,000 rows) stays quick: each call reads only what it touches.

MemberRole
of_set_string (string as_path, string as_value) → longWRITES a string at as_path, escaped for you. The path may have SEVERAL levels (a/b/c): the missing ones are created as OBJECTS, and an EMPTY document becomes one — writing IS building. A value already there is replaced, wherever it sits. A key that holds a / is written ~1 in the path (of_escape_key). A loop of writes in a big document stays quick. Returns 0, -4 when a level on the way is a scalar, or an array without that index (an array is crossed by an existing index: items/1/qty), -5 on an empty path or an index 0 on an array. A null string is written empty; for a JSON null: of_set_raw(as_path, "null")
of_set_number (string as_path, long al_value) → longWRITES a NUMBER at as_path, the decimal point forced whatever the region. Five overloads: integer, long, longlong, decimal, double — a decimal and a longlong keep EVERY digit (the cents of an amount, the last digit of an id above 2^53), and a literal such as 690.50 is a decimal, written 690.5. Same path rule and same codes as of_set_string; a null value writes null
of_set_boolean (string as_path, boolean ab_value) → longWRITES a BOOLEAN at as_path: true or false, never quoted. Same path rule and same codes as of_set_string; a null value writes null
of_set_raw (string as_path, string as_json) → longWRITES at as_path a value ALREADY in JSON — a nested object, an array, null — as it is: you vouch for it. This is also how an array is OPENED before of_add_*. Same path rule and same codes as of_set_string; a null or empty one writes null
of_remove (string as_path) → longRemoves the value at the path — an object member (its key and one comma go with it) or an array element; the result stays valid. Returns 0, -4 when nothing is there, -5 on an empty path or an index 0 on an array
// Local variables
n_pbt_json  lnv_json

// Load, edit in place, then read back the ONE document with of_text
lnv_json.of_load(/*json*/ '{"name":"Ada","active":false}')
lnv_json.of_set_string(/*path*/ "name", /*value*/ "Grace")   // replace
lnv_json.of_set_boolean(/*path*/ "active", /*value*/ true)
lnv_json.of_set_number(/*path*/ "count", /*value*/ 3)        // added : the key was absent

// lnv_json.of_text() -> {"name":"Grace","active":true,"count":3}

Dates #

A date is written and read as ISO 8601 — what APIs, browsers and Python write —, whatever the date format of the machine: a date "2026-10-10", a datetime "2026-10-10T14:30:00" (its fraction of second follows when it is not zero), a time "14:30:00". A null value writes null.

MethodRole
of_set_date (string as_path, date ad_value) → longWRITES a date at as_path (yyyy-mm-dd). Same path rule as of_set_string: returns 0, -4 when a level on the way is a scalar or an array without that index, -5 on an empty path or an index 0 on an array
of_set_datetime (string as_path, datetime adt_value) → longWRITES a datetime (yyyy-mm-ddThh:mm:ss, the fraction of second after a point when it is not zero). No time zone is written: the hour is the one given. Returns 0, -4 or -5, as of_set_date
of_set_time (string as_path, time at_value) → longWRITES a time of day (hh:mm:ss). Returns 0, -4 or -5, as of_set_date
of_add_date (string as_path, date ad_value) → longAppends a date to the ARRAY at as_path. Returns 0, -4 when as_path is missing or is not an array, -5 when a level of as_path is an index 0 on an array
of_add_datetime (string as_path, datetime adt_value) → longAppends a datetime to the ARRAY at as_path. Returns 0, -4 or -5, as of_add_date
of_add_time (string as_path, time at_value) → longAppends a time of day to the ARRAY at as_path. Returns 0, -4 or -5, as of_add_date
of_get_date (string as_path) → dateThe date written at the path ("2026-10-10", or the date of a datetime). 1900-01-01 when the value is absent, is not a date or names a day that does not exist (2026-02-30); of_exists and of_type tell an absent value
of_get_datetime (string as_path) → datetimeThe datetime written at the path: a T or a space between date and time, with or without seconds, with a fraction of second, or a date alone (midnight). A Z or an offset (+02:00) is read and not applied: the hour is the one written — convert it yourself when the zone matters. 1900-01-01 00:00:00 when absent or not a date
of_get_time (string as_path) → timeThe time written at the path ("14:30", "14:30:00", "14:30:00.250", or the time of a datetime). 00:00:00 when absent, not a time, or a date alone
// Local variables
n_pbt_json  lnv_json
date        ld_due
datetime    ldt_created

// Write : ISO 8601, whatever the date format of the machine
lnv_json.of_set_date(/*path*/ "due", /*value*/ Date(2026, 10, 31))
lnv_json.of_set_datetime(/*path*/ "created", /*value*/ DateTime(Date(2026, 10, 10), Time(14, 30, 0)))

// Read what an API writes : the Z is read, the hour stays the one written
lnv_json.of_load(/*json*/ '{"due":"2026-10-31","created":"2026-10-10T14:30:00Z"}')
ld_due      = lnv_json.of_get_date(/*path*/ "due")
ldt_created = lnv_json.of_get_datetime(/*path*/ "created")

A DataStore as JSON, and back #

PowerBuilder has ExportJson and ImportJson only from 2017 R2 on. n_pbt_json does it from PowerBuilder 10, typed by the columns: a string, a number (a decimal keeps every digit), a date yyyy-mm-dd, a datetime, a time, null for a NULL. One overload takes a datawindow, another a datawindowchild.

MethodRole
of_from_datastore (string as_path, datastore ads_source) → longWRITES the rows of the primary buffer as an ARRAY of objects at as_path: one object per row, one member per column, named as the column. The empty path makes the DOCUMENT that array (the body of a bulk call). Returns the number of rows written, -5 when the DataStore is not valid or has no column, else what of_set_raw returns
of_to_datastore (string as_path, datastore ads_target) → longINSERTS the ARRAY of objects at as_path into a DataStore: one row per object, each member in the column of the same name (case ignored), converted to the type of the column — a number from a number or from a string that is all a number, a date, a datetime or a time from an ISO text, NULL for a null or a value the column cannot hold. A member that names no column is ignored, an element that is not an object makes no row. The rows are NEW: Update inserts them. Returns the number of rows inserted, -5 when the DataStore is not valid or has no column, -4 when there is no array at as_path
// Local variables
n_pbt_json  lnv_json
datastore   lds_orders
long        ll_rows
string      ls_body

// Send : every row of a retrieved DataStore as typed objects - the body of the call
lds_orders = create datastore
lds_orders.DataObject = "d_orders"
lds_orders.SetTransObject(SQLCA)
lds_orders.Retrieve()
ll_rows = lnv_json.of_from_datastore(/*path*/ "orders", /*source*/ lds_orders)
ls_body = lnv_json.of_text()

// Receive : the objects of a response inserted as NEW rows, ready for Update
lds_orders.Reset()
lnv_json.of_load(/*json*/ '{"data":[{"id":7,"customer":"Grace","amount":"19.99","due":"2026-12-24"}]}')
ll_rows = lnv_json.of_to_datastore(/*path*/ "data", /*target*/ lds_orders)
destroy lds_orders

When the text is not JSON #

MethodRole
of_get_last_error ( ) → stringWhy the last call returned a negative code; after a refused of_load, where the text is wrong and why: line 2, column 6 : ':' expected after a key. Empty after a call that succeeded
of_error_position ( ) → longReturns the position, counted from 1 in the text given to the last of_load (a BOM included), of the character where that text goes wrong — to select in an editor; 0 when the last of_load succeeded
// Local variables
n_pbt_json  lnv_json
long        ll_rc
string      ls_why
long        ll_where

// A response with a colon missing on its second line
ll_rc = lnv_json.of_load(/*json*/ '{"id":4152,~r~n "paid" true}')
IF ll_rc < 0 THEN
	ls_why   = lnv_json.of_get_last_error()     // line 2, column 9 : ':' expected after a key
	ll_where = lnv_json.of_error_position()     // 22
END IF

Scope. JSON parses in pure PowerScript, no page or engine: n_pbt_json stays autoinstantiate and works everywhere, PowerBuilder 10 included. For XML, see xml (which relies on the engine); to display a JSON as a collapsible tree, see jsontree.

← Component reference · Guide contents