Vely logo install | documentation | examples | changelog
16.10.0 released May 10, 2023
read-json

Purpose: Parse JSON text and get values.

// Random access to JSON data:

read-json <json> key <key> \
    value [ define ] <value> \
    [ status [ define ] <status> ]
    [ type [ define ] <type> ]

// Sequential access to JSON data:

read-json <json> traverse begin

read-json <json> traverse \
    key [ define ] <key> \
    value [ define ] <value> \
    [ status [ define ] <status> ] \
    [ type [ define ] <type> ]

read-json will obtain the values from <json> variable that was produced by parsing JSON text with new-json.
Without "traverse" clause
key/value pairs are accessed with "key" clause specifying a normalized key name, which is the value's name preceded with the names of all objects and array members leading up to it, separated by a comma, with each name quoted. The actual <value> is obtained with "value" clause, and the <type> of value can be obtained with optional "type" clause.

The <status> variable in "status" clause is either VV_OKAY on success (meaning value is obtained) or VV_ERR_EXIST if not found. The value is stored in string variable <value> specified in "value" clause.  

<value>, <type> and <status> can be created with optional "define".

Optional number <type> in "type" clause will contain the type of data, which can be VV_JSON_TYPE_STRING, VV_JSON_TYPE_NUMBER, VV_JSON_TYPE_REAL, VV_JSON_TYPE_BOOL or VV_JSON_TYPE_NULL  for a string, integer, decimal, boolean and null value respectively.
With "traverse" clause
This allows sequential access to JSON values. No key is specified, rather key/value pairs are obtained in sequence from beginning to the end. <key> can be created with optional "define". The other clauses used are the same as without "traverse".
Random access via built-in hash
You can access randomly any number, string, boolean or null value within a JSON document without having to create a matching structure in your program. Internally, Vely creates a hash table for fast and direct access to any key/value pair (see new-json).
Keys (normalized names)
<key> in "key" clause is a normalized name of any given leaf node in JSON text. This means every non-leaf node is included (such as arrays and objects), separated by a dot ("."), and arrays are indexed with "[]". An example would be:
"menu"."popup"."menuitem"[1]."onclick"

For instance, the above is a normalized name in this JSON text with the value of "OpenDoc()":
{"menu": {
  "id": "file",
  "value": "File",
  "popup": {
    "menuitem": [
      {"value": "New", "onclick": "CreateNewDoc()"},
      {"value": "Open", "onclick": "OpenDoc()"},
      {"value": "Close", "onclick": "CloseDoc()"}
    ]
  }
}

the normalized names for all leaf nodes are:
"menu"."id"
"menu"."value"
"menu"."popup"."menuitem"[0]."value"
"menu"."popup"."menuitem"[0]."onclick"
"menu"."popup"."menuitem"[1]."value"
"menu"."popup"."menuitem"[1]."onclick"
"menu"."popup"."menuitem"[2]."value"
"menu"."popup"."menuitem"[2]."onclick"

The code to parse this JSON text might then look like:
// Json text
char json_text[] =\
"{\"menu\":\
{\"id\": \"file\",\
\"value\": \"File\",\
\"popup\":\
    {\"menuitem\":\
        [{\"value\": \"New\", \"onclick\": \"CreateNewDoc()\"},\
        {\"value\": \"Open\", \"onclick\": \"OpenDoc()\"},\
        {\"value\": \"Close\", \"onclick\": \"CloseDoc()\"}\
        ]\
    }\
}\
}\n";

// Parse json text
new-json define json_var from json_text status define st error-text define errt error-position define errp
if (st != VV_OKAY) {
    @Could not parse json, error <<p-out errt>> at position <<p-num errp>>
} else {
    @Json parsed okay.
}

// Get value based on key
read-json json_var key "menu"."popup"."menuitem"[1]."onclick" value define val status st
if (st != VV_OKAY) {
    @Could not find json key
} else {
    @Key value is <<p-out val>>
}

The result is:
Json parsed okay.
Key value is OpenDoc()

Values
All values are always returned as strings. This is because in many cases that's how they are used in an application and converting them to other types (like numbers) and back again would affect performance.

Vely checks type validity - for example an invalid integer or decimal number will produce an error. If you need to convert a value to a number you can use C's library functions like atoll() or atof().

String values are returned encoded as UTF8, and any escaped characters (like \n or \t) are converted to their binary equivalent (use "noencode" in new-json to skip this). Such encoded strings can be output anywhere, from a terminal to a client (like browser).
Walk through JSON document
Use "traverse" clause to access JSON nodes sequentially, one by one, from the beginning to the end. Use "traverse begin" to rewind to the beginning, and then read data using "traverse" with "key" clause.

The following code loops through all the leaf nodes of a JSON document - you can also use it to examine a JSON document with unknown structure:
// Parse JSON
new-json define jv from json_text

// Position at the beginning of the JSON document
read-json jv traverse begin

// Loop through all JSON leaf nodes, obtain normalized name, type and value
while (1)
{
    // Get nodes' key, value and type. Check status for end of loop detection.
    read-json jv traverse key define k value define v type define t status define s

    if (s != VV_OKAY) break; // end of loop

    // Output name, value and type
    pf-out "Name [%s], value [%s], type [%lld]\n", k, v, t
}

Examples
read-json jv key "glossary"."title" value d

See also
JSON ( delete-json   new-json   read-json  )  SEE ALL (documentation)


Copyright (c) 2017-2023 Dasoftver LLC